DatasetSupport (TableSupport, PagingSupport, CurrentDataSupport) 추가

master
mjkhan21 3 months ago
parent e337b0cc7a
commit 6e7649bc16

@ -120,6 +120,23 @@ ul.nav-tabs > li.nav-item {
display: inline-block;
}
.sortable, .sort-asc, .sort-desc {
cursor: pointer;
}
.sortable::after, .sort-asc::after, .sort-desc::after {
color: #b2babb;
float: inline-end;
}
.sortable::after {
content: '\002B27'; /* #11047, 2B27 */
}
.sort-asc::after {
content: '\002BC5'; /* #11205, 2BC5 */
}
.sort-desc::after {
content: '\002BC6'; /* #11206, 2BC6 */
}
.avatar {
width: auto;
}

@ -632,9 +632,9 @@ $.fn.setPaging = function(config) {
return this.each(function(){
let length = config.list ? config.list.length : config.dataLength,
let length = config.list ? config.list.length : config.dataSize || config.dataLength,
empty = length < 1,
start = empty ? 0 : config.start + 1,
start = empty ? 0 : !config.added ? config.start + 1 : 1,
end = empty ? 0 : config.start + length,
pagingInfo = empty ? "" : start + " ~ " + numberFormat.format(end) + " / " + numberFormat.format(config.totalSize),
selector = "#"+ config.prefix + "PagingInfo,[name='" + config.prefix + "PagingInfo']";
@ -673,7 +673,7 @@ $.fn.setCurrentRow = function(val) {
var e = $(this);
e.find("tr").each(function(){
var tr = $(this),
current = val == tr.attr("data-key");
current = val == (tr.attr("data-key") || tr.attr("data-index"));
if (current)
tr.addClass("current-row");
else
@ -800,3 +800,83 @@ function inputsInRange(fromSource, toSource) {
function ignore() {
console.log.apply(console, arguments);
}
/**Utility to simplify the document object query.
*/
class DomQuery {
/**Sets the selectors to the containers.
* @param {string} containers selectors(comma-separated) to the containers
* @returns {DomQuery} this DomQuery
*/
setContainers(containers = "") {
this.containers = containers.split(",").filter(str => !isEmpty(str));
return this;
}
/**Returns a selector prepended with the selectors to the containers.
* @param {...string} selectors
* To get a selector prepended with the selectors to the containers
* <pre><code>let doq = new DomQuery().setContainers("div#divID,form#formID"),
* selector = doq.selector("input[type='text']");</code></pre>
* To get a selector with the given attribute value prepended with the selectors to the containers.
* <pre><code>selector = doq.selector("attribute-name", "attribute-value");</code></pre>
* @returns {string} selector prepended with the selectors to the containers
*/
selector(...selectors) {
let result = null;
if (!this.containers)
this.containers = [];
let selector = "";
switch (selectors.length) {
case 1: selector = selectors[0]; break;
case 2: selector = "[" + selectors[0] + "='" + selectors[1] + "']"; break;
default: break;
}
if (this.containers.length < 1)
result = selector;
if (!result && !selector.includes(","))
result = this.containers
.map(container => selector ? container + " " + selector : container)
.join(",");
if (!result && selector.includes(",")) {
selector = selector.split(",");
result = this.containers.reduce((acc, x) => [...acc, ...selector.map(y => x + " " + y)], []).join(",");
}
if (this.trace)
log("selector:", result);
return result;
}
/**Selects an element matching the given selector.
* @param {...string} args
* To get an element matching the given selector
* <pre><code>let doq = new DomQuery().setContainers("div#divID,form#formID"),
* found = doq.find("selector to the desired element");</code></pre>
* To get an element with the given attribute value.
* <pre><code>found = doq.find("attribute-name", "attribute-value");</code></pre>
* @returns {object} element matching the given selector
*/
find(...args) {
return document.querySelector(this.selector(...args));
}
/**Selects elements matching the given selector.
* @param {...string} args
* To get elements matching the given selector
* <pre><code>let doq = new DomQuery().setContainers("div#divID,form#formID"),
* found = doq.findAll("selector to the desired elements");</code></pre>
* To get elements with the given attribute value.
* <pre><code>found = doq.findAll("attribute-name", "attribute-value");</code></pre>
* @returns {array} elements matching the given selector
*/
findAll(...args) {
return Array.from(document.querySelectorAll(this.selector(...args)));
}
}

@ -0,0 +1,387 @@
/* Copyright (c) 2020 Emjay Khan. All rights reserved. */
/**Base class of DatasetSupport.
* It has empty implementations of methods called back in response to a Dataset's events.
* A DatasetSupport extension implements some of the call-back methods to achieve its purpose.
*/
class DatasetSupport {
/**Creates a DatasetSupport with the given configuration.
* @param {object} conf configuration.
* <ul><li>css selector for a containing element, optional</li>
* <li>css selector for elements the DatasetSupport works with primarily</li>
* </ul>
*/
constructor(conf) {
this.selector = conf.selector || null;
this.dataset = conf.ctrl.dataset;
this._doq = conf.ctrl.doq;
}
get doq() {
return this._doq;
}
init() {}
get keymapped() {
return this.dataset ? this.dataset.keymapped : false;
}
getSelector(...selectors) {
return this.doq.selector(...selectors);
}
/**
* @param {string} selector
* @param {boolean} strict
*/
find(...selector) {
return this.doq.find(...selector);
}
findAll(...selector) {
return this.doq.findAll(...selector);
}
}
class TableSupport extends DatasetSupport {
constructor(conf) {
super(conf);
this.selector = conf.table;
this.tr = conf.tr;
this.notFound = conf.notFound;
this.formatter = conf.formatter;
this.selectionToggler = conf.selectionToggler || "";
this.refreshOnModify = conf.refreshOnModify || [];
this.init();
}
init() {
this.body = this.find(this.selector + " tbody");
if (this.tr) {
let template = this.find(this.tr);
this.tr = (template || {}).innerHTML || "";
}
if (this.notFound) {
let template = this.find(this.notFound);
this.notFound = (template || {}).innerHTML || "";
}
if (!this.tr && !this.notFound) {
let templates = this.findAll(this.selector + " template");
if (templates.length < 1)
log("WARNING: ", this.selector + " must have a template for a data row");
this.tr = (templates[0] || {}).innerHTML;
this.notFound = (templates[1] || {}).innerHTML;
}
let sort = evt => {
let th = evt.target;
this.dataset.sort(th.getAttribute("data-sort"));
};
this.sortables().forEach(th => th.addEventListener("click", sort));
}
sortables() {
return this.findAll(this.selector + " thead th[data-sort]");
}
updateSortables(sorter) {
this.sortables().forEach(th => {
th.classList.remove(
TableSupport.cssClass.sortable,
TableSupport.cssClass.asc,
TableSupport.cssClass.desc
);
if (th.getAttribute("data-sort") == sorter.by)
th.classList.add(TableSupport.cssClass[sorter.order]);
else
th.classList.add(TableSupport.cssClass.sortable);
});
if (sorter.by)
this.draw();
}
/**Handler called back on the dataset change event.
* @param {Dataset} dataset this Dataset
* @param {object} option optional information
*/
renderList(option = {}) {
this.draw(option);
this.findAll(".enable-onfound").forEach(e => e.disabled = this.dataset.empty);
}
draw(option = {}) {
let empty = this.dataset.empty,
trs = !empty ? this.dataset.inStrings(this.tr, this.formatter) : [this.notFound];
this.body.innerHTML = trs.join("");
if (!this.selectionToggler) return;
let toggler = this.find(this.selectionToggler);
toggler.checked = false;
toggler.disabled = empty;
}
/**Handler called back on the current change event.
* @param {DataItem} item current DataItem
*/
setCurrentRow(item) {
if (!item) return;
let index = item.index;
this.findAll(this.selector + " tbody tr").forEach(tr => {
let dataIndex = tr.getAttribute("data-index"),
current = index == dataIndex;
if (current)
tr.classList.add(TableSupport.cssClass.current);
else
tr.classList.remove(TableSupport.cssClass.current);
});
}
/**Handler called back on the selection change event
* @param {array} selected selected DataItems
*/
setSelections(selected) {
let selectedIndex = selected.map(item => item.index);
this.findAll(this.selector + " tbody input[name='data-index']")
.forEach(input => {
input.checked = selectedIndex.includes(input.value)
});
this.findAll(".enable-onselect")
.forEach(e => e.disabled = selected.length < 1);
}
/**Handler called back on the modify event
* @param {array} changed names of the changed properties
* @param {DataItem} item owner DataItem of the changed properties
* @param {boolean} current whether the event is on the current item
*/
updateModified(changed) {
if (this.refreshOnModify.length < 1) return;
let refresh = false;
for (let prop of changed) {
refresh = this.refreshOnModify.includes(prop);
if (refresh)
break;
}
if (!refresh) return;
this.draw();
this.dataset.setState();
}
}
TableSupport.cssClass = {
current: "current-row",
sortable: "sortable",
asc: "sort-asc",
desc: "sort-desc"
};
class CurrentDataSupport extends DatasetSupport {
constructor(conf) {
super(conf);
if (!this.selector)
this.selector = "[data-field]";
}
type(input) {
return input ? (input.getAttribute("type") || input.tagName).toLowerCase() : "";
}
dataField(input) {
return input.getAttribute("data-field");
}
property(input) {
return this.dataField(input)
|| input.name
|| input.id;
}
update(input, value) {
switch (this.type(input)) {
case "radio":
case "checkbox":
if (isEmpty(value)) {
input.checked = false;
} else {
switch (typeof value) {
case "string":
input.checked = input.value == value || value.split(",").includes(input.value);
break;
case "number":
case "boolean":
input.checked = input.value == value.toString();
break;
default:
if (value instanceof Array)
input.checked = value.includes(input.value);
break;
}
}
break;
case "select":
for (let option of input.options) {
option.selected = option.value === value;
}
break;
case "img": input.src = value; break;
case "button": input.innerHTML = value; break;
default:
if (input.value !== undefined)
input.value = value;
else
input.innerHTML = value;
}
}
setChanged(evt) {
let input = evt.target,
type = this.type(input),
prop = this.property(input),
val = input.value;
switch (type) {
case "checkbox":
case "radio":
let attrs = input.getAttributeNames()
.filter(attr => ['data-field', 'name'].includes(attr))
.map(attr => "[" + attr + "=\"" + input.getAttribute(attr) + "\"]")
.join("");
let inputs = this.findAll("[type=\"" + type + "\"]" + attrs);
switch (inputs.length) {
case 0: return;
case 1:
let cb = inputs[0];
if (cb.checked) break;
switch (val) {
case "true": val = "false"; break;
case "y": val = "n"; break;
case "Y": val = "N"; break;
case "yes": val = "no"; break;
default: val = ""; break;
}
break;
default:
val = inputs.filter(cb => cb.checked).map(cb => cb.value).join(",");
break;
}
break;
default: break;
}
this.dataset.setValue(prop, val);
}
setCurrent(item) {
if (!item) return;
this.findAll(this.selector).forEach(input => {
let prop = this.property(input);
if (!prop) return;
let evt = !["checkbox", "radio"].includes(this.type(input)) ? "change" : "click",
handler = (evt) => this.setChanged(evt)
input.removeEventListener(evt, handler);
this.update(input, item.getValue(prop));
input.addEventListener(evt, handler);
});
this.enableOnDirty(item);
this.enableOnNew(item);
}
updateModified(changed, item) {
if (!item) return;
this.findAll(this.selector).forEach(input => {
let prop = this.property(input);
if (!changed.includes(prop)) return;
this.update(input, item.getValue(prop));
});
this.enableOnDirty(item);
}
/**Enables the HTML elements of '.enable-ondirtyitem' if the item is dirty.
* @param {DataItem} item a DataItem
*/
enableOnDirty(item) {
let dirty = item.dirty;
this.findAll(".enable-ondirtyitem")
.forEach(e => e.disabled = !dirty);
}
/**Enables the HTML elements of '.enable-onnewitem' if the item is new.
* @param {DataItem} item a DataItem
*/
enableOnNew(item) {
let isnew = item.isNew();
this.findAll(".enable-onnewitem")
.forEach(e => e.disabled = !isnew);
}
newData(init) {
let data = this.findAll(this.selector).reduce((data, input) => {
let prop = this.property(input);
if (prop)
data[prop] = null;
return data;
}, {});
if (init)
init(data);
return data;
}
getData(item = this.dataset.getCurrent("item")) {
return this.findAll(this.selector).reduce((data, input) => {
let dataField = this.dataField(input),
property = input.getAttribute("name") || input.getAttribute("id"),
val = item.data[dataField];
if (property && val !== undefined)
data[property] = val;
return data;
}, {});
}
}
class PagingSupport extends DatasetSupport {
constructor(conf) {
super(conf);
this.prefix = conf.ctrl.prefix;
this.sizeOffset = conf.sizeOffset || 0;
this.linkContainer = conf.linkContainer;
this.func = conf.func;
this.statusContainer = conf.statusContainer;
this.statusContent = conf.statusContent;
}
setPaging(option) {
let pagination = option ? option.pagination : null;
if (!pagination) return;
pagination.prefix = this.prefix;
pagination.dataSize = pagination.dataSize + this.sizeOffset;
if (this.linkContainer) {
pagination.func = this.func;
pagination.added = option.added;
$(this.doq.selector(this.linkContainer)).setPaging(pagination);
}
}
}

@ -187,20 +187,13 @@ ValueFormat.InvalidValue = "^invalid^value^";
/**Wraps a user data and traces the manipulation performed on it and consequent status.
*/
class DataItem {
/** user data */
data;
/** value formatters */
_formats;
/** whether the user data is selected or not */
selected;
/** state of the user data */
state;
/**Creates a new DataItem.
* @param {any} data user data
* @param {object} formats value formatters of the user data's property
*/
constructor(data, formats) {
this.index = null;
this.no = null;
this.data = data;
this._formats = formats;
this.selected = false;
@ -292,6 +285,16 @@ class DataItem {
return parsed;
}
/**Replaces the current data with the new data and resets the state.
* @param {any} data new data
* @returns the DataItem
*/
replace(data) {
this.data = data;
this.state = null;
return this;
}
/**Returns a string converted from the template using the property values of the user data.
* In the template, placeholder for the properties of the user data is specified like {property name}.
* @param {string} template template string
@ -303,10 +306,18 @@ class DataItem {
if (formatter) {
str = formatter(str, this);
}
let empty = Object.entries(this.data).length < 1
if (!empty)
for (let p in this.data) {
let regexp = this._formats.regexp(p);
str = str.replace(regexp, this.getValue(p));
}
str = str.replace(/{data-index}/gi, this.index)
.replace(/{data-no}/gi, this.no);
if (empty)
str = str.replace(/{([^}]+)}/g, "");
return str;
}
}
@ -357,15 +368,6 @@ class DataItem {
* </p>
*/
class Dataset {
_items;
_byKeys;
_current;
/**Dataset configuration
*/
conf;
_formats;
/**Creates a new Dataset with a configuration.
* The configuration is an object with which you specify
* <ul> <li>keymapper - function that returns a key of a user data. Used to identify user data in the Dataset. Mandatory.</li>
@ -386,17 +388,20 @@ class Dataset {
* </ul>
* @param conf {object} configuration
*/
constructor(conf) {
constructor(conf = {}) {
this._items = [];
this.keys = conf.keys || [];
this._byKeys = {};
this._current = null;
this.conf = notEmpty(conf, "conf is required but missing");
notEmpty(conf.keymapper, "keymapper is required but missing");
let keymapper = conf.keymapper;
if (keymapper)
conf.keymapper = info => {
return keymapper(info) || info._tmpKey;
};
this.keymapped = !isEmpty(this.conf.keymapper);
this._formats = new ValueFormat(conf.formats);
this._sorter = {by: ""};
this.dataBinder = dataBinder.create(this, conf.doctx);
@ -404,6 +409,7 @@ class Dataset {
if (!conf.trace)
this.log = () => {};
[ "onDatasetChange",
"onCurrentChange",
"onSelectionChange",
@ -428,12 +434,20 @@ class Dataset {
}
/**Returns the key of a user data.
* @param {any|DataItem} info user data or {@link DataItem dataItem} of a user data
* @param {any|DataItem} item user data or {@link DataItem dataItem} of a user data
* @returns {string} key of a user data
*/
getKey(info) {
let data = info ? info.data || info : null;
return data ? this.conf.keymapper(data) : null;
getKey(item) {
if (!item)
return null;
let dataItem = item instanceof DataItem,
info = dataItem ? item.data : item;
if (this.keymapped)
return this.conf.keymapper(info);
else if (dataItem)
return item.index;
throw "Unable to determine the key of " + info;
}
/**Returns keys of the Dataset's user data.
@ -457,7 +471,7 @@ class Dataset {
* let removed = dirties.removed;
*/
getKeys(status){
let dataset = this.getDataset(status);
let dataset = this.getDataset(status, "item");
if ("dirty" != status)
return dataset.map(e => this.getKey(e));
@ -479,9 +493,20 @@ class Dataset {
* let dataItem = dataset.getData("key-0", "item");
*/
getData(key, option) {
let item = this._byKeys["key-" + key];
if (this.empty)
return null;
let item = null;
if (this.keymapped) {
item = this._byKeys["key-" + key];
if (!item)
item = this.getTempItem();
} else {
let index = key,
found = this._items.filter(item => index == item.index);
item = found.length > 0 ? found[0] : null;
}
if (!item || item.unreachable)
return null;
return "item" == option ? item : item.data;
@ -500,28 +525,17 @@ class Dataset {
* @param {object} obj optional information
* @returns {Dataset} the Dataset
*/
setData(obj, option) {
setData(obj, option = {}) {
this._byKeys = {};
this._current = null;
obj = obj || {};
let data = this._getDataItems(obj);
let data = this._getDataItems(obj, option);
this._items = data.items;
this._byKeys = data.byKeys;
this._sorter = {by: ""};
/*
obj = obj || {};
let array = Array.isArray(obj) ? obj : this.conf.dataGetter(obj) || [];
if (!Array.isArray(array))
throw new Error("The data must be an array");
this._items = array.map(e => new DataItem(e, this._formats));
this._items.forEach(item => {
let key = "key-" + this.getKey(item.data);
this._byKeys[key] = item;
});
*/
this.onDatasetChange(obj, option);
this.setState(obj.state);
this.onDirtiesChange(this.dirty);
@ -529,17 +543,34 @@ class Dataset {
return this;
}
_getDataItems(obj) {
_getDataItems(obj, option) {
obj = obj || {};
let array = Array.isArray(obj) ? obj : this.conf.dataGetter(obj) || [];
if (!Array.isArray(array))
throw new Error("The data must be an array");
let _items = array.map(e => new DataItem(e, this._formats)),
_byKeys = {};
_items.forEach(item => {
let prefix = "ndx-" + new Date().getTime(),
_items = array.map(e => new DataItem(e, this._formats)),
_byKeys = {},
length = this._items.length,
noStart = 0;
if (option.pagination)
noStart = option.pagination.start || 0;
else {
if (length > 0) {
let last = this._items[length - 1];
noStart = last.no;
}
}
_items.forEach((item, index) => {
if (this.keymapped) {
let key = "key-" + this.getKey(item.data);
_byKeys[key] = item;
}
item.index = prefix + index;
item.no = ++noStart;
});
return {
@ -561,12 +592,13 @@ class Dataset {
* @param {object} obj optional information
* @returns {Dataset} the Dataset
*/
addData(obj, option) {
addData(obj, option = {}) {
if (this.empty)
return this.setData(obj);
return this.setData(obj, option);
let state = this.state;
let data = this._getDataItems(obj);
option.added = true;
let data = this._getDataItems(obj, option);
this._items = this._items.concat(data.items);
this._byKeys = {
...this._byKeys,
@ -614,10 +646,10 @@ class Dataset {
this._items.sort((item0, item1) => {
let val0 = (item0.data || {})[by],
val1 = (item1.data || {})[by];
if (val0 === undefined || val1 === undefined)
throw "Property not found: " + by;
if (val0 === undefined || val1 === undefined) return 0;
if (isEmpty(val0) && isEmpty(val1)) return 0;
if (!this._sorter.asc)
[val0, val1] = [val1, val0];
@ -689,10 +721,18 @@ class Dataset {
}
getTempItem(current) {
let found = this._items.filter(item => item.data._tmpKey);
let found = this._items.filter(item => item.data._tmpKey || item.isNew());
found = found.length > 0 ? found[0] : null;
if (this.keymapped) {
if (current)
this.setCurrent();
} else {
if (found && current) {
this._current = found;
this.dataBinder.onCurrentChange(found);
this.onCurrentChange(found);
}
}
return found;
}
@ -726,8 +766,9 @@ class Dataset {
get state() {
let empty = this.empty,
self = this;
return {
currentKey:!empty ? self.getKey(self.getCurrent()) : null,
currentKey:!empty ? self.getKey(self.getCurrent("item")) : null,
selectedKeys:!empty ? self.getKeys("selected") : []
};
}
@ -753,8 +794,9 @@ class Dataset {
this.onDirtiesChange(false);
} else {
state = state || this.state;
let current = this.getData(state.currentKey) || this.getDataset()[0],
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);
@ -896,7 +938,8 @@ class Dataset {
fire = args[2];
}
if (dirty || fire) {
this.onSelectionChange(this.getDataset("selected"));
let selected = this.getDataset("selected", this.keymapped ? undefined : "item");
this.onSelectionChange(selected);
}
return dirty;
}
@ -914,8 +957,10 @@ class Dataset {
*/
toggle(key) {
let item = this.getData(key, "item"),
status = item ? item.toggle() : false;
this.onSelectionChange(this.getDataset("selected"));
status = item ? item.toggle() : false,
selected = this.getDataset("selected", this.keymapped ? undefined : "item");
this.onSelectionChange(selected)
return status;
}
@ -934,26 +979,41 @@ class Dataset {
if (!data) return this;
let found = this.getTempItem(true);
if (found) {
if (found)
return found;
}
let notDirty = !this.dirty,
array = Array.isArray(data) ? data : [data];
array.forEach(e => {
array = Array.isArray(data) ? data : [data],
now = new Date().getTime(),
length = this._items.length,
noStart = 0,
added = [];
if (length > 0) {
let last = this._items[length - 1];
noStart = last.no;
}
array.forEach((e, index) => {
let item = new DataItem(e, this._formats);
this._items.push(item);
if (this.keymapped) {
let key = this.getKey(e);
if (!key)
e._tmpKey = key = new Date().getTime()
e._tmpKey = key = now + index;
this._byKeys["key-" + key] = item;
} else {
item.index = ("ndx-" + now) + index;
item.no = ++noStart;
}
item.state = "added";
added.push(item);
});
let state = this.state;
this.onAppend(array);
state.currentKey = this.getKey(array[array.length - 1]);
let last = added[added.length - 1];
state.currentKey = this.keymapped ? this.getKey(last.data) : last.index;
this.setState(state);
if (notDirty)
@ -1057,31 +1117,44 @@ class Dataset {
if (isEmpty(replacement)) return this;
let replacements = Array.isArray(replacement) ? replacement : [replacement],
replacing = [];
replacing = [],
getKey = obj => {
return this.keymapped ?
(obj.key || this.getKey(obj.data)) :
this.keys.reduce((acc, cur) => {
acc[cur] = obj.data[cur];
return acc;
}, {}
);
},
getItem = key => {
if (this.keymapped)
return this.getData(key, "item");
let entries = Object.entries(key),
found = this._items.filter(item => {
for (let e of entries) {
let k = e[0],
v = e[1];
if (v != item.data[k])
return false;
}
return true;
});
return found.length < 1 ? null : found[0];
};
replacements.forEach(obj => {
let data = obj.data;
if (!data) return;
let key = obj.key || this.getKey(data);
let key = getKey(obj);
if (!key) return;
let oldItem = this.getData(key, "item"),
newItem = new DataItem(data, this._formats),
pos = oldItem ? this._items.indexOf(oldItem) : -1;
newItem.selected = oldItem && oldItem.selected;
if (pos > -1)
this._items[pos] = newItem;
else
this._items.push(newItem);
delete this._byKeys["key-" + key];
this._byKeys["key-" + this.getKey(data)] = newItem;
if (this._current == oldItem)
this._current = newItem;
let item = getItem(key);
if (!item) return;
replacing.push(newItem);
item.replace(data);
replacing.push(item);
});
this.onReplace(replacing);
this.setState();
@ -1108,7 +1181,7 @@ class Dataset {
let before = this.dirty,
keys = Array.isArray(key) ? key : [key],
removed = this._items.filter(item => {
let k = this.getKey(item.data),
let k = this.getKey(this.keymapped ? item.data : item),
remove = keys.includes(k);
if (remove) {
item.state = "added" == item.state ? "ignore" : "removed";
@ -1167,7 +1240,7 @@ class Dataset {
let before = this.dirty,
keys = Array.isArray(key) ? key : [key],
erased = this._items.filter(item => {
let k = this.getKey(item.data),
let k = this.getKey(this.keymapped ? item.data : item),
erase = keys.indexOf(k) > -1;
if (erase) {
delete this._byKeys["key-" + k];
@ -1216,7 +1289,7 @@ class Dataset {
let dataset = this.getDataset("item");
return dataset.filter(item => !item.data._tmpKey)
.map(item => item.inString(template, formatter));
.map((item, index) => item.inString(template, formatter));
}
/**Returns a property value of user data.
@ -1231,20 +1304,24 @@ class Dataset {
*/
getValue(...args) {
let key = null,
property = null;
property = null,
item = null;
switch (args.length) {
case 1:
key = this.getKey(this.getCurrent());
//key = this.getKey(this.getCurrent());
property = args[0];
item = this.getCurrent("item");
break;
case 2:
key = args[0];
property = args[1];
item = this.getData(key, "item");
break;
default: return null;
}
let item = this.getData(key, "item");
// let item = this.getData(key, "item");
return item ? item.getValue(property) : undefined;
}
@ -1268,7 +1345,7 @@ class Dataset {
value = null;
switch (args.length) {
case 2:
key = this.getKey(this.getCurrent());
key = this.getKey(this.getCurrent(this.keymapped ? undefined : "item"));
property = args[0];
value = args[1];
break;
@ -1279,6 +1356,7 @@ class Dataset {
break;
default: return this;
}
return this.modify(key, function(item){
return item.setValue(property, value);
});
@ -1338,10 +1416,15 @@ class Dataset {
class DatasetControl {
constructor(conf) {
notEmpty(conf.keymapper, "keymapper");
this.prefix = conf.prefix;
this.prefixName = conf.prefixName;
this.doctx = conf.doctx || "";
// this.doctx = conf.doctx || "";
this.doq = new DomQuery().setContainers(conf.doctx);
if (conf.addOns) {
conf.addOns.forEach(addOn => addOn.doq = this.doq);
}
this.infoSize = conf.infoSize;
this.appendData = conf.appendData;
@ -1360,6 +1443,7 @@ class DatasetControl {
*/
this.onModify(props, modified, current);
};
conf.onDirtiesChange = dirty => this.onDirtiesChange(dirty);
conf.onReplace = obj => this.onReplace(obj);
conf.onSort = status => this.onSort(status);
@ -1374,6 +1458,10 @@ class DatasetControl {
};
}
get addOns() {
return this.dataset.addOns;
}
prefixed(str) {
return (this.prefix || "") + str;
}
@ -1387,8 +1475,7 @@ class DatasetControl {
this._load();
}
_load(option) {
option = option || {};
_load(option = {}) {
if (!this.query.pageNum)
this.query.pageNum = 1;
@ -1443,11 +1530,22 @@ class DatasetControl {
});
}
setData(obj, option) {
setData(obj, option = {}) {
this.setPaging(obj, option);
this.dataset.setData(obj, option);
}
addData(obj, option) {
setPaging(obj, option) {
let prefix = this.prefix || obj.prefix;
if (!prefix || !obj[prefix + "Paging"]) return;
option.pagination = obj[prefix + "Paging"];
option.pagination.prefix = prefix;
delete obj[prefix + "Paging"];
}
addData(obj, option = {}) {
this.setPaging(obj, option);
this.dataset.addData(obj, option);
}
@ -1463,8 +1561,11 @@ class DatasetControl {
return this.dataset.getCurrent(option);
}
toObject(item) {
return this.dataBinder().toObject(item || this.getCurrent("item"));
toObject(item = this.getCurrent("item")) {
if (this.dataset.keymapped)
return this.dataBinder().toObject(item);
if (this.addOns.currentData)
return this.addOns.currentData.getData(item);
}
dataBinder() {
@ -1515,6 +1616,7 @@ class DatasetControl {
setInfo(info) {}
newInfo(obj) {
if (this.dataset.keymapped)
this.dataset.append(obj || {});
this.getInfo();
}
@ -1535,6 +1637,10 @@ class DatasetControl {
debug("on modify", props, "modified", modified, "current", current);
}
onDirtiesChange(dirty) {
debug("on dirties change", dirty);
}
onReplace(replacing) {
debug("on replace", replacing);
}
@ -1585,23 +1691,15 @@ class DatasetControl {
}
selector(selector) {
return this.dataBinder().selector(selector);
}
querySelector(selector) {
return this.dataBinder().querySelector(selector);
}
querySelectorAll(selector) {
return this.dataBinder().querySelectorAll(selector);
return this.doq.selector(selector);
}
find(name) {
return this.querySelector(!name.startsWith("#") ? "[name='" + name + "']" : name);
find(...args) {
return this.doq.find(...args);
}
findAll(name) {
return this.querySelectorAll("[name='" + name + "']");
findAll(...args) {
return this.doq.findAll(...args);
}
}
@ -1644,7 +1742,7 @@ var dataBinder = {
},
create: (dataset, doctx) => {
if (!dataset || isEmpty(doctx))
if (!dataset || isEmpty(doctx) || !dataset.keymapped)
return {
onCurrentChange: item => {},
onModify: (changed, item) => {},
@ -1708,6 +1806,7 @@ var dataBinder = {
});
};
obj.onModify = (changed, item) => {
return;
obj.inputs().forEach(input => {
let prop = dataBinder.dataMap(input) || dataBinder.property(input);
if (!changed.includes(prop)) return;
@ -1739,26 +1838,26 @@ var dataBinder = {
*/
function tableSorter(ctrl, selector) {
let obj = {
asc: "&#11205",
desc: "&#11206",
asc: "sort-asc",
desc: "sort-desc",
sortable: "sortable",
ctrl: ctrl,
headers: () => ctrl.querySelectorAll(selector),
headers: () => ctrl.findAll(selector),
sort: (evt) => {
let th = evt.target;
ctrl.sort(th.getAttribute("data-field"));
ctrl.sort(th.getAttribute("data-sort"));
}
};
obj.setHeaders = (sorter) => {
obj.headers().forEach(th => {
let inner = th.innerHTML;
inner = inner.replace(/ ⯅/gi, "").replace(/ ⯆/gi, "");
if (th.getAttribute("data-field") == sorter.by)
inner += " " + obj[sorter.order] || "";
th.innerHTML = inner;
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) => {
@ -1768,23 +1867,8 @@ function tableSorter(ctrl, selector) {
};
obj.headers().forEach(th => {
th.style["cursor"] = "pointer";
th.removeEventListener("click", obj.sort);
th.addEventListener("click", obj.sort);
});
return obj;
/*
asc: {style: "sorting_asc", attr: "ascending"},
desc: {style: "sorting_desc", attr: "descending"},
setHeaders: (headers, sorter) => {
headers.forEach(th => {
th.classList.remove("sorting_asc", "sorting_desc");
th.removeAttribute("aria-sort");
if (th.getAttribute("data-field") != sorter.by) return;
th.classList.add(sorting[sorter.order].style);
th.setAttribute("aria-sort", sorting[sorter.order].attr);
});
}
*/
}
Loading…
Cancel
Save