|
|
|
@ -404,7 +404,6 @@ class Dataset {
|
|
|
|
|
|
|
|
|
|
this._formats = new ValueFormat(conf.formats);
|
|
|
|
|
this._sorter = {by: ""};
|
|
|
|
|
this.dataBinder = dataBinder.create(this, conf.doctx);
|
|
|
|
|
|
|
|
|
|
if (!conf.trace)
|
|
|
|
|
this.log = () => {};
|
|
|
|
@ -729,7 +728,6 @@ class Dataset {
|
|
|
|
|
} else {
|
|
|
|
|
if (found && current) {
|
|
|
|
|
this._current = found;
|
|
|
|
|
this.dataBinder.onCurrentChange(found);
|
|
|
|
|
this.onCurrentChange(found);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -751,7 +749,6 @@ class Dataset {
|
|
|
|
|
this._current = item;
|
|
|
|
|
|
|
|
|
|
if (diff || fire) {
|
|
|
|
|
this.dataBinder.onCurrentChange(item);
|
|
|
|
|
this.onCurrentChange(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -794,12 +791,21 @@ class Dataset {
|
|
|
|
|
this.onDirtiesChange(false);
|
|
|
|
|
} else {
|
|
|
|
|
state = state || this.state;
|
|
|
|
|
let current = this.getData(state.currentKey, "item") || this.getDataset("item")[0],
|
|
|
|
|
currentKey = this.getKey(current);
|
|
|
|
|
|
|
|
|
|
this.onSort(this.sorter);
|
|
|
|
|
this.setCurrent(currentKey, true);
|
|
|
|
|
this.select(state.selectedKeys || [], true, true);
|
|
|
|
|
if (!state.byKeyValue) {
|
|
|
|
|
let current = this.getData(state.currentKey, "item") || this.getDataset("item")[0],
|
|
|
|
|
currentKey = this.getKey(current);
|
|
|
|
|
|
|
|
|
|
this.onSort(this.sorter);
|
|
|
|
|
this.setCurrent(currentKey, true);
|
|
|
|
|
this.select(state.selectedKeys || [], true, true);
|
|
|
|
|
} else {
|
|
|
|
|
let currentKey = this.indexOf(state.currentKey)[0],
|
|
|
|
|
selectedKeys = this.indexOf(...state.selectedKeys);
|
|
|
|
|
|
|
|
|
|
this.onSort(this.sorter);
|
|
|
|
|
this.setCurrent(currentKey, true);
|
|
|
|
|
this.select(selectedKeys, true, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
@ -1072,13 +1078,11 @@ class Dataset {
|
|
|
|
|
if (changed.length > 0) {
|
|
|
|
|
if (!item.state)
|
|
|
|
|
item.state = "modified";
|
|
|
|
|
this.dataBinder.onModify(changed, item);
|
|
|
|
|
this.onModify(changed, item, current);
|
|
|
|
|
if (notDirty)
|
|
|
|
|
this.onDirtiesChange(true);
|
|
|
|
|
} else if (revert) {
|
|
|
|
|
changed = Object.getOwnPropertyNames(data);
|
|
|
|
|
this.dataBinder.onModify(changed, item);
|
|
|
|
|
this.onModify(changed, item, current);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1361,6 +1365,39 @@ class Dataset {
|
|
|
|
|
return item.setValue(property, value);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyValues(...args) {
|
|
|
|
|
return args.map(arg =>
|
|
|
|
|
this.keys.reduce((obj, k) => {
|
|
|
|
|
obj[k] = arg[k];
|
|
|
|
|
return obj;
|
|
|
|
|
}, {})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
indexOf(...keyValues) {
|
|
|
|
|
let ofKeys = item => {
|
|
|
|
|
let data = item.data;
|
|
|
|
|
|
|
|
|
|
for (let kv of keyValues) {
|
|
|
|
|
let equal = true;
|
|
|
|
|
for (let entry of Object.entries(kv)) {
|
|
|
|
|
let k = entry[0],
|
|
|
|
|
v = entry[1];
|
|
|
|
|
equal = equal && data[k] == v;
|
|
|
|
|
if (!equal)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (equal)
|
|
|
|
|
return equal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
return this._items
|
|
|
|
|
.filter(item => ofKeys(item))
|
|
|
|
|
.map(item => item.index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**Called back when user data are set.
|
|
|
|
|
* @param {object|array} obj object that has user data or an array of user data
|
|
|
|
@ -1498,14 +1535,29 @@ class DatasetControl {
|
|
|
|
|
|
|
|
|
|
let all = option.all,
|
|
|
|
|
prev = option.prev,
|
|
|
|
|
state = this.dataset.state;
|
|
|
|
|
state = this.dataset.keymapped ? this.dataset.state : null;
|
|
|
|
|
if (!state) {
|
|
|
|
|
state = this.empty ? {currentKey: null, selectedKeys: []} : null;
|
|
|
|
|
if (!state) {
|
|
|
|
|
state = {};
|
|
|
|
|
|
|
|
|
|
let current = this.getCurrent(),
|
|
|
|
|
selected = this.getDataset("selected");
|
|
|
|
|
current = this.dataset.keyValues(current);
|
|
|
|
|
selected = this.dataset.keyValues(...selected);
|
|
|
|
|
|
|
|
|
|
state.currentKey = current.length > 0 ? current[0] : null;
|
|
|
|
|
state.selectedKeys = selected;
|
|
|
|
|
state.byKeyValue = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
option.state = state;
|
|
|
|
|
|
|
|
|
|
if (!all) {
|
|
|
|
|
this._load(option);
|
|
|
|
|
} else {
|
|
|
|
|
let query = Object.assign({}, this.query);
|
|
|
|
|
this.query.fetchSize = (query.pageNum || 1) * query.fetchSize;
|
|
|
|
|
this.query.fetchSize = this.dataset.length; //(query.pageNum || 1) * query.fetchSize;
|
|
|
|
|
this.query.pageNum = 1;
|
|
|
|
|
option.callback = () => {this.query = query;};
|
|
|
|
|
this._load(option);
|
|
|
|
@ -1556,15 +1608,6 @@ class DatasetControl {
|
|
|
|
|
getCurrent(option) {
|
|
|
|
|
return this.dataset.getCurrent(option);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toObject(item = this.getCurrent("item")) {
|
|
|
|
|
if (this.dataset.keymapped)
|
|
|
|
|
return this.dataBinder().toObject(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataBinder() {
|
|
|
|
|
return this.dataset.dataBinder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCurrent(key) {
|
|
|
|
|
this.dataset.setCurrent(key);
|
|
|
|
@ -1597,7 +1640,6 @@ class DatasetControl {
|
|
|
|
|
size:this.infoSize,
|
|
|
|
|
init:() => {
|
|
|
|
|
let current = this.getCurrent("item");
|
|
|
|
|
this.dataBinder().onCurrentChange(current);
|
|
|
|
|
this.setInfo(current);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
@ -1695,174 +1737,4 @@ class DatasetControl {
|
|
|
|
|
findAll(...args) {
|
|
|
|
|
return this.doq.findAll(...args);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dataBinder = {
|
|
|
|
|
type: input => input ? (input.getAttribute("type") || input.tagName).toLowerCase() : "",
|
|
|
|
|
dataMap: input => input.getAttribute("data-map"),
|
|
|
|
|
property: input => input.name || input.id,
|
|
|
|
|
|
|
|
|
|
toArray(val) {
|
|
|
|
|
if (isEmpty(val))
|
|
|
|
|
return [];
|
|
|
|
|
if (Array.isArray(val))
|
|
|
|
|
return val;
|
|
|
|
|
if ("string" == typeof val)
|
|
|
|
|
return val.split(",");
|
|
|
|
|
return [val];
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setValue: (input, value) => {
|
|
|
|
|
value = value || "";
|
|
|
|
|
switch (dataBinder.type(input)) {
|
|
|
|
|
case "radio":
|
|
|
|
|
case "checkbox":
|
|
|
|
|
input.checked = dataBinder.toArray(value).includes(input.value);
|
|
|
|
|
break;
|
|
|
|
|
case "select":
|
|
|
|
|
for (let option of input.options) {
|
|
|
|
|
option.selected = option.value == value;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "img":
|
|
|
|
|
if (!(input.src || "").endsWith(value))
|
|
|
|
|
input.src = value; break;
|
|
|
|
|
default:
|
|
|
|
|
if (input.value !== undefined)
|
|
|
|
|
input.value = value;
|
|
|
|
|
else
|
|
|
|
|
input.innerHTML = value;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
create: (dataset, doctx) => {
|
|
|
|
|
if (!dataset || isEmpty(doctx) || !dataset.keymapped)
|
|
|
|
|
return {
|
|
|
|
|
onCurrentChange: item => {},
|
|
|
|
|
onModify: (changed, item) => {},
|
|
|
|
|
toObject: () => null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let obj = {
|
|
|
|
|
selector: (selector) => doctx ? doctx.split(",").map(str => str + " " + selector).join(",") : selector,
|
|
|
|
|
querySelector: (selector) => document.querySelector(obj.selector(selector)),
|
|
|
|
|
querySelectorAll: (selector) => Array.from(document.querySelectorAll(obj.selector(selector))),
|
|
|
|
|
inputs: () => obj.querySelectorAll("[data-map]"),
|
|
|
|
|
|
|
|
|
|
inputValue: (input) => {
|
|
|
|
|
let val = input.value;
|
|
|
|
|
switch (dataBinder.type(input)) {
|
|
|
|
|
case "radio":
|
|
|
|
|
return input.checked ? val : undefined;
|
|
|
|
|
case "checkbox":
|
|
|
|
|
let checks = obj.querySelectorAll("[name='" + input.name + "']");
|
|
|
|
|
switch (checks.length) {
|
|
|
|
|
case 0: return undefined;
|
|
|
|
|
case 1:
|
|
|
|
|
if (input.checked)
|
|
|
|
|
return val;
|
|
|
|
|
if ("Y" == val)
|
|
|
|
|
return "N";
|
|
|
|
|
if ('true' == val)
|
|
|
|
|
return 'false';
|
|
|
|
|
return undefined;
|
|
|
|
|
default:
|
|
|
|
|
return checks
|
|
|
|
|
.filter(input => input.checked)
|
|
|
|
|
.map(input => input.value)
|
|
|
|
|
.join(",");
|
|
|
|
|
}
|
|
|
|
|
case "img": return input.src;
|
|
|
|
|
default: return val !== undefined ? val : input.innerHTML;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
setChanged: (evt) => {
|
|
|
|
|
let input = evt.target,
|
|
|
|
|
prop = dataBinder.dataMap(input) || dataBinder.property(input),
|
|
|
|
|
inputVal = obj.inputValue(input);
|
|
|
|
|
if (undefined === inputVal) return;
|
|
|
|
|
|
|
|
|
|
dataset.setValue(prop, inputVal);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
obj.onCurrentChange = (current) => {
|
|
|
|
|
if (!current) return;
|
|
|
|
|
|
|
|
|
|
obj.inputs().forEach(input => {
|
|
|
|
|
let prop = dataBinder.dataMap(input) || dataBinder.property(input);
|
|
|
|
|
if (!prop) return;
|
|
|
|
|
|
|
|
|
|
let evt = !["checkbox", "radio"].includes(dataBinder.type(input)) ? "change" : "click";
|
|
|
|
|
input.removeEventListener(evt, obj.setChanged);
|
|
|
|
|
dataBinder.setValue(input, current.getValue(prop));
|
|
|
|
|
input.addEventListener(evt, obj.setChanged);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
obj.onModify = (changed, item) => {
|
|
|
|
|
return;
|
|
|
|
|
obj.inputs().forEach(input => {
|
|
|
|
|
let prop = dataBinder.dataMap(input) || dataBinder.property(input);
|
|
|
|
|
if (!changed.includes(prop)) return;
|
|
|
|
|
|
|
|
|
|
dataBinder.setValue(input, item.getValue(prop));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
obj.toObject = (item) => {
|
|
|
|
|
let result = {};
|
|
|
|
|
obj.inputs().forEach(input => {
|
|
|
|
|
let dataMap = dataBinder.dataMap(input),
|
|
|
|
|
property = dataBinder.property(input) || dataMap,
|
|
|
|
|
value = item.data[dataMap || property];
|
|
|
|
|
|
|
|
|
|
if (value !== undefined)
|
|
|
|
|
result[property] = value;
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**<table../>로 표시되는 데이터셋의 데이터를 정렬하고 UI에 반영되도록 설정한다.
|
|
|
|
|
* @param ctrl {DatasetControl} DatasetControl
|
|
|
|
|
* @param selector 테이블 헤더에 대한 selector
|
|
|
|
|
*/
|
|
|
|
|
function tableSorter(ctrl, selector) {
|
|
|
|
|
let obj = {
|
|
|
|
|
asc: "sort-asc",
|
|
|
|
|
desc: "sort-desc",
|
|
|
|
|
sortable: "sortable",
|
|
|
|
|
|
|
|
|
|
ctrl: ctrl,
|
|
|
|
|
|
|
|
|
|
headers: () => ctrl.findAll(selector),
|
|
|
|
|
sort: (evt) => {
|
|
|
|
|
let th = evt.target;
|
|
|
|
|
ctrl.sort(th.getAttribute("data-sort"));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
obj.setHeaders = (sorter) => {
|
|
|
|
|
obj.headers().forEach(th => {
|
|
|
|
|
th.classList.remove(obj.sortable, obj.asc, obj.desc);
|
|
|
|
|
if (th.getAttribute("data-sort") == sorter.by)
|
|
|
|
|
th.classList.add(obj[sorter.order]);
|
|
|
|
|
else
|
|
|
|
|
th.classList.add(obj.sortable);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
ctrl.onSort = (sorter) => {
|
|
|
|
|
if (ctrl.renderList)
|
|
|
|
|
ctrl.renderList();
|
|
|
|
|
obj.setHeaders(sorter);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
obj.headers().forEach(th => {
|
|
|
|
|
th.removeEventListener("click", obj.sort);
|
|
|
|
|
th.addEventListener("click", obj.sort);
|
|
|
|
|
});
|
|
|
|
|
return obj;
|
|
|
|
|
}
|