|
|
|
@ -383,7 +383,12 @@ class Dataset {
|
|
|
|
|
|
|
|
|
|
this.conf = notEmpty(conf, "conf is required but missing");
|
|
|
|
|
notEmpty(conf.keymapper, "keymapper is required but missing");
|
|
|
|
|
let keymapper = conf.keymapper;
|
|
|
|
|
conf.keymapper = info => {
|
|
|
|
|
return keymapper(info) || info._tmpKey;
|
|
|
|
|
};
|
|
|
|
|
this._formats = new ValueFormat(conf.formats);
|
|
|
|
|
this._dataBinder = dataBinder.create(this, conf.inputs);
|
|
|
|
|
|
|
|
|
|
if (!conf.trace)
|
|
|
|
|
this.log = () => {};
|
|
|
|
@ -463,6 +468,8 @@ class Dataset {
|
|
|
|
|
*/
|
|
|
|
|
getData(key, option) {
|
|
|
|
|
let item = this._byKeys["key-" + key];
|
|
|
|
|
if (!item)
|
|
|
|
|
item = this.getTempItem();
|
|
|
|
|
if (!item || item.unreachable)
|
|
|
|
|
return null;
|
|
|
|
|
return "item" == option ? item : item.data;
|
|
|
|
@ -481,7 +488,6 @@ class Dataset {
|
|
|
|
|
* @returns {Dataset} the Dataset
|
|
|
|
|
*/
|
|
|
|
|
setData(obj) {
|
|
|
|
|
let state = this.state;
|
|
|
|
|
this._byKeys = {};
|
|
|
|
|
this._current = null;
|
|
|
|
|
|
|
|
|
@ -503,8 +509,7 @@ class Dataset {
|
|
|
|
|
});
|
|
|
|
|
*/
|
|
|
|
|
this.onDatasetChange(obj);
|
|
|
|
|
this.setState(obj.state || state);
|
|
|
|
|
// this.setState(!Array.isArray(obj) ? obj.state : state);
|
|
|
|
|
this.setState(obj.state);
|
|
|
|
|
this.onDirtiesChange(this.dirty);
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
@ -601,6 +606,14 @@ class Dataset {
|
|
|
|
|
return null;
|
|
|
|
|
return "item" == option ? current : current.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getTempItem(current) {
|
|
|
|
|
let found = this._items.filter(item => item.data._tmpKey);
|
|
|
|
|
found = found.length > 0 ? found[0] : null;
|
|
|
|
|
if (current)
|
|
|
|
|
this.setCurrent();
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**Sets the user data as current that is associated with the key.
|
|
|
|
|
* @param {string} key key to a user data
|
|
|
|
@ -616,8 +629,10 @@ class Dataset {
|
|
|
|
|
|
|
|
|
|
this._current = item;
|
|
|
|
|
|
|
|
|
|
if (diff || fire)
|
|
|
|
|
if (diff || fire) {
|
|
|
|
|
this._dataBinder.onCurrentChange(item);
|
|
|
|
|
this.onCurrentChange(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**Returns the Dataset's current state in an object.
|
|
|
|
@ -835,6 +850,11 @@ class Dataset {
|
|
|
|
|
append(data) {
|
|
|
|
|
if (!data) return this;
|
|
|
|
|
|
|
|
|
|
let found = this.getTempItem(true);
|
|
|
|
|
if (found) {
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let notDirty = !this.dirty,
|
|
|
|
|
array = Array.isArray(data) ? data : [data];
|
|
|
|
|
array.forEach(e => {
|
|
|
|
@ -842,6 +862,8 @@ class Dataset {
|
|
|
|
|
this._items.push(item);
|
|
|
|
|
|
|
|
|
|
let key = this.getKey(e);
|
|
|
|
|
if (!key)
|
|
|
|
|
e._tmpKey = key = new Date().getTime()
|
|
|
|
|
this._byKeys["key-" + key] = item;
|
|
|
|
|
item.state = "added";
|
|
|
|
|
});
|
|
|
|
@ -907,11 +929,14 @@ 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) {
|
|
|
|
|
this.onModify(Object.getOwnPropertyNames(data), item, current);
|
|
|
|
|
changed = Object.getOwnPropertyNames(data);
|
|
|
|
|
this._dataBinder.onModify(changed, item);
|
|
|
|
|
this.onModify(changed, item, current);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
@ -948,8 +973,7 @@ class Dataset {
|
|
|
|
|
replace(replacement) {
|
|
|
|
|
if (isEmpty(replacement)) return this;
|
|
|
|
|
|
|
|
|
|
let before = this.dirty,
|
|
|
|
|
replacements = Array.isArray(replacement) ? replacement : [replacement],
|
|
|
|
|
let replacements = Array.isArray(replacement) ? replacement : [replacement],
|
|
|
|
|
replacing = [];
|
|
|
|
|
replacements.forEach(obj => {
|
|
|
|
|
let data = obj.data;
|
|
|
|
@ -977,10 +1001,7 @@ class Dataset {
|
|
|
|
|
replacing.push(newItem);
|
|
|
|
|
});
|
|
|
|
|
this.onReplace(replacing);
|
|
|
|
|
let after = this.dirty;
|
|
|
|
|
if (before != after)
|
|
|
|
|
this.onDirtiesChange(after);
|
|
|
|
|
|
|
|
|
|
this.setState();
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1109,7 +1130,9 @@ class Dataset {
|
|
|
|
|
* @returns {array} array of strings converted from the template using the property values of the user data
|
|
|
|
|
*/
|
|
|
|
|
inStrings(template, formatter) {
|
|
|
|
|
return this.getDataset("item")
|
|
|
|
|
let dataset = this.getDataset("item");
|
|
|
|
|
|
|
|
|
|
return dataset.filter(item => !item.data._tmpKey)
|
|
|
|
|
.map(item => item.inString(template, formatter));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1229,21 +1252,23 @@ class DatasetControl {
|
|
|
|
|
notEmpty(conf.keymapper, "keymapper");
|
|
|
|
|
this.prefix = conf.prefix;
|
|
|
|
|
this.prefixName = conf.prefixName;
|
|
|
|
|
this._doctx = conf.doctx || "";
|
|
|
|
|
this.infoSize = conf.infoSize;
|
|
|
|
|
this.appendData = conf.appendData;
|
|
|
|
|
|
|
|
|
|
this.query = {};
|
|
|
|
|
|
|
|
|
|
conf.onDatasetChange = obj => this.onDatasetChange(obj),
|
|
|
|
|
conf.onCurrentChange = item => this.onCurrentChange(item),
|
|
|
|
|
conf.onSelectionChange = selected => this.onSelectionChange(selected),
|
|
|
|
|
conf.onAppend = items => this.onAppend(items),
|
|
|
|
|
conf.onDatasetChange = obj => this.onDatasetChange(obj);
|
|
|
|
|
conf.onCurrentChange = item => this.onCurrentChange(item);
|
|
|
|
|
conf.onSelectionChange = selected => this.onSelectionChange(selected);
|
|
|
|
|
conf.onAppend = items => this.onAppend(items);
|
|
|
|
|
conf.onModify = (props, modified, current) => {
|
|
|
|
|
let info = this.dataset.getCurrent("item");
|
|
|
|
|
if (!info || "added" == info.state)
|
|
|
|
|
return;
|
|
|
|
|
this.onModify(props, modified, current);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
conf.onReplace = obj => this.onReplace(obj);
|
|
|
|
|
|
|
|
|
|
this.dataset = new Dataset(conf);
|
|
|
|
|
|
|
|
|
@ -1269,14 +1294,18 @@ class DatasetControl {
|
|
|
|
|
this._load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_load() {
|
|
|
|
|
_load(state, prev) {
|
|
|
|
|
if (!this.query.pageNum)
|
|
|
|
|
this.query.pageNum = 1;
|
|
|
|
|
if (prev)
|
|
|
|
|
this.query.pageNum = Math.max(1, this.query.pageNum - 1);
|
|
|
|
|
|
|
|
|
|
ajax.get({
|
|
|
|
|
url:this.urls.load,
|
|
|
|
|
data:this.query,
|
|
|
|
|
success:resp => {
|
|
|
|
|
if (!prev)
|
|
|
|
|
resp.state = state;
|
|
|
|
|
if (!this.appendData || this.query.pageNum == 1)
|
|
|
|
|
this.setData(resp);
|
|
|
|
|
else {
|
|
|
|
@ -1286,6 +1315,10 @@ class DatasetControl {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reload(prev) {
|
|
|
|
|
this._load(this.dataset.state, prev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
download(type) {
|
|
|
|
|
this.query.download = type || "xls";
|
|
|
|
|
let query = toQuery(this.query);
|
|
|
|
@ -1308,6 +1341,10 @@ class DatasetControl {
|
|
|
|
|
getCurrent(option) {
|
|
|
|
|
return this.dataset.getCurrent(option);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentToObject() {
|
|
|
|
|
return this.dataset._dataBinder.toObject(this.getCurrent("item"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCurrent(key) {
|
|
|
|
|
this.dataset.setCurrent(key);
|
|
|
|
@ -1326,7 +1363,6 @@ class DatasetControl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getInfo(params) {
|
|
|
|
|
let info = this.dataset.getCurrent("item");
|
|
|
|
|
if (this.urls.getInfo)
|
|
|
|
|
ajax.get({
|
|
|
|
|
url:this.urls.getInfo,
|
|
|
|
@ -1339,12 +1375,16 @@ class DatasetControl {
|
|
|
|
|
title: this.prefixName + " 정보",
|
|
|
|
|
content:resp,
|
|
|
|
|
size:this.infoSize,
|
|
|
|
|
init:() => this.setInfo(info)
|
|
|
|
|
init:() => {
|
|
|
|
|
let current = this.getCurrent("item");
|
|
|
|
|
this.dataset._dataBinder.onCurrentChange(current);
|
|
|
|
|
this.setInfo(current);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
else
|
|
|
|
|
this.setInfo(info);
|
|
|
|
|
this.setInfo(this.getCurrent("item"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setInfo(info) {}
|
|
|
|
@ -1370,6 +1410,10 @@ class DatasetControl {
|
|
|
|
|
debug("on modify", props, "modified", modified, "current", current);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onReplace(replacing) {
|
|
|
|
|
debug("on replace", replacing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
save(info) {
|
|
|
|
|
if (!info) return;
|
|
|
|
|
let item = this.getCurrent("item"),
|
|
|
|
@ -1386,7 +1430,7 @@ class DatasetControl {
|
|
|
|
|
if (resp.saved) {
|
|
|
|
|
dialog.alert("저장됐습니다.");
|
|
|
|
|
dialog.close(this.prefixed("dialog"));
|
|
|
|
|
this._load();
|
|
|
|
|
this.reload();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1408,41 +1452,108 @@ class DatasetControl {
|
|
|
|
|
|
|
|
|
|
onRemove(selected, resp) {
|
|
|
|
|
if (resp.saved)
|
|
|
|
|
this._load();
|
|
|
|
|
this.reload(selected.length == this.dataset.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bindInputs(obj, selector) {
|
|
|
|
|
let inputs = ["input", "select", "textarea"].map(tag => selector + " " + tag).join(","),
|
|
|
|
|
setChanged = evt => {
|
|
|
|
|
let input = evt.target,
|
|
|
|
|
name = input.getAttribute("data-map"),
|
|
|
|
|
val = input.value;
|
|
|
|
|
this.setValue(name, val);
|
|
|
|
|
_selector(selector) {
|
|
|
|
|
let doctx = this._doctx;
|
|
|
|
|
return doctx ? "*[data-doctx='" + doctx + "'] " + selector : selector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
querySelector(selector) {
|
|
|
|
|
return document.querySelector(this._selector(selector));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
querySelectorAll(selector) {
|
|
|
|
|
return document.querySelectorAll(this._selector(selector));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dataBinder = {
|
|
|
|
|
type: input => {
|
|
|
|
|
return input ? (input.getAttribute("type") || input.tagName).toLowerCase() : "";
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
dataMap: input => {
|
|
|
|
|
return input.getAttribute("data-map");
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
property: input => {
|
|
|
|
|
return input.name || input.id;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setValue: (input, value) => {
|
|
|
|
|
value = value || "";
|
|
|
|
|
switch (dataBinder.type(input)) {
|
|
|
|
|
case "radio":
|
|
|
|
|
case "checkbox": input.checked = value && value == 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, selector) => {
|
|
|
|
|
if (!dataset || isEmpty(selector))
|
|
|
|
|
return {
|
|
|
|
|
onCurrentChange: item => {},
|
|
|
|
|
onModify: (changed, item) => {},
|
|
|
|
|
toObject: () => null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll(inputs).forEach(input => {
|
|
|
|
|
let prop = input.getAttribute("data-map")
|
|
|
|
|
|| input.name
|
|
|
|
|
|| input.id;
|
|
|
|
|
if (!prop) return;
|
|
|
|
|
let obj = {
|
|
|
|
|
selector: selector,
|
|
|
|
|
setChanged: (evt) => {
|
|
|
|
|
let input = evt.target,
|
|
|
|
|
prop = dataBinder.dataMap(input),
|
|
|
|
|
val = input.value;
|
|
|
|
|
dataset.setValue(prop, val);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
input.removeEventListener("change", setChanged);
|
|
|
|
|
obj.onCurrentChange = (current) => {
|
|
|
|
|
if (!current) return;
|
|
|
|
|
|
|
|
|
|
let dataItem = obj instanceof DataItem,
|
|
|
|
|
value = dataItem ? obj.getValue(prop) : obj[prop],
|
|
|
|
|
inputType = (input.type || input.tagName || "").toLowerCase();
|
|
|
|
|
document.querySelectorAll(obj.selector).forEach(input => {
|
|
|
|
|
let prop = dataBinder.dataMap(input);
|
|
|
|
|
if (!prop) return;
|
|
|
|
|
|
|
|
|
|
input.removeEventListener("change", obj.setChanged);
|
|
|
|
|
dataBinder.setValue(input, current.getValue(prop));
|
|
|
|
|
input.addEventListener("change", obj.setChanged);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
obj.onModify = (changed, item) => {
|
|
|
|
|
document.querySelectorAll(obj.selector).forEach(input => {
|
|
|
|
|
let prop = dataBinder.dataMap(input);
|
|
|
|
|
if (!changed.includes(prop)) return;
|
|
|
|
|
|
|
|
|
|
dataBinder.setValue(input, item.getValue(prop));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
obj.toObject = (item) => {
|
|
|
|
|
|
|
|
|
|
switch (inputType) {
|
|
|
|
|
case "radio": input.checked = value && value == input.value; break;
|
|
|
|
|
case "checkbox": input.checked = value && value == input.value; break;
|
|
|
|
|
case "select":
|
|
|
|
|
for(let option of input.options) {
|
|
|
|
|
option.selected = option.value == value;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default: input.value = ifEmpty(value, ""); break;
|
|
|
|
|
}
|
|
|
|
|
input.addEventListener("change", setChanged);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let result = {};
|
|
|
|
|
document.querySelectorAll(obj.selector).forEach(input => {
|
|
|
|
|
let dataMap = dataBinder.dataMap(input),
|
|
|
|
|
property = dataBinder.property(input) || dataMap;
|
|
|
|
|
|
|
|
|
|
result[property] = item.data[dataMap];
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
};
|