(function() {
/**
* @namespace DataFunctions
* @memberof Help4.jscore
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.ToObject
* @returns {Object}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.Equals
* @param {Help4.jscore.ControlBase} instance - the class instance to do the comparison with
* @returns {boolean}
*/
/**
* <b>WARNING:</b> This function does NOT deliver by-value but by-reference!
* @typedef {Function} Help4.jscore.DataFunctions.Get
* @param {string[]} keys - keys to select the data output
* @returns {Object}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.Set
* @param {Object} json
* @param {Object} [params = {}]
* @param {boolean} [params.allowReadonlyOverride = false] - allow to write readonly parameters
* @returns {Help4.jscore.ControlBase}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.Clone
* @returns {Help4.jscore.ControlBase}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.GetKeys
* @returns {string[]}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.GetConfig
* @param {string} name - name of the config param
* @returns {Object|null}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.ForEach
* @param {function} executor - function to execute on every data item
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.Every
* @param {function} executor - function to execute on every data item
* @returns {boolean}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.Merge
* @param {Help4.jscore.ControlBase} instance - the to be merged instance
* @param {function} mergeFn - the merge function
* @returns {Help4.jscore.ControlBase}
*/
/**
* @typedef {Function} Help4.jscore.DataFunctions.GetTexts
* @returns {Object}
*/
/**
* definition of all data functions
* @typedef {Object} Help4.jscore.DataFunctions.Embedded
* @property {Help4.jscore.DataFunctions.ToObject} toObject - exports class data to an object
* @property {Help4.jscore.DataFunctions.Equals} equals - compares class data with another instance
* @property {Help4.jscore.DataFunctions.Get} get - get certain class data
* @property {Help4.jscore.DataFunctions.Set} set - set certain class data
* @property {Help4.jscore.DataFunctions.Clone} clone - clone instance with its data
* @property {Help4.jscore.DataFunctions.GetKeys} getKeys - deliver all data keys
* @property {Help4.jscore.DataFunctions.GetConfig} getConfig - deliver a certain parameter configuration
* @property {Help4.jscore.DataFunctions.ForEach} forEach - execute a function on every data item
* @property {Help4.jscore.DataFunctions.Every} every - execute a function on every data item
* @property {Help4.jscore.DataFunctions.Merge} merge - merge with another instance and deliver the merged result
* @property {Help4.jscore.DataFunctions.GetTexts} getTexts - get all texts
* @property {Help4.EmbeddedEvent} onChange - listener for data change events
*/
Help4.jscore.DataFunctions = {
toObject: function() {
const {PARAM_KEYS, DATA_KEYS} = this.constructor;
const classConfig = this.____getClassConfig();
const {[PARAM_KEYS]: paramKeys, [DATA_KEYS]: dataKeys, params} = classConfig;
const json = {};
paramKeys?.forEach(key => {
const accessKey = params[key].private ? `__${key}` : key;
json[key] = this[accessKey]; // use getter
});
dataKeys?.forEach(key => json[key] = this[key]); // use getter
return json;
},
equals: function(instance) {
if (this === instance) return true;
if (!(instance instanceof this.constructor)) return false;
const {PARAM_KEYS, DATA_KEYS, EQUALS, CUSTOM_TYPE} = this.constructor;
const classConfig = this.____getClassConfig();
const {[PARAM_KEYS]: paramKeys, [DATA_KEYS]: dataKeys, params, data} = classConfig;
const compare = key => {
const {private: p, type, equals} = params[key]; // "private" reserved by JSDoc
const accessKey = p ? `__${key}` : key;
if (!type) throw new Error(`Properties need to define a type: ${key}`);
const funEquals = type === CUSTOM_TYPE ? equals : EQUALS[type];
if (!funEquals) throw new Error(`No "equals" function defined: ${key}`);
return funEquals.call(this, this[accessKey], instance[accessKey]);
}
for (const key of paramKeys || []) {
if (!compare(key)) return false;
}
for (const key of dataKeys || []) {
if (!compare(key)) return false;
}
return true;
},
get: function(keys= []) {
const data = this.____getData();
const result = {};
keys.forEach(key => result[key] = data[key]);
return result;
},
set: function(json, {allowReadonlyOverride = false} = {}) {
if (json === this) return this; // is not a JSON but myself
// is not a JSON but another instance of my class
if (json instanceof this.constructor) {
json = json.dataFunctions.toObject();
}
const {params = {}, data = {}} = this.____getClassConfig();
for (const [key, value] of Object.entries(json)) {
if (data.hasOwnProperty(key)) {
this[key] = value; // use setter
} else if (params.hasOwnProperty(key)) {
const {private: p, readonly} = params[key]; // "private" reserved by JSDoc
const accessKey = p ? `__${key}` : key;
if (readonly) {
// readonly parameters only to be overridden if specifically requested
// setter not used here, therefore no data update callbacks!
if (allowReadonlyOverride) this.____getData()[key] = value;
} else {
this[accessKey] = value; // use setter
}
}
}
return this;
},
clone: function() {
const data = this.dataFunctions.toObject();
return new this.constructor(data);
},
getKeys: function() {
const {PARAM_KEYS, DATA_KEYS} = this.constructor;
const {[PARAM_KEYS]: paramKeys = [], [DATA_KEYS]: dataKeys = []} = this.____getClassConfig();
return paramKeys.concat(dataKeys);
},
getConfig: function(name) {
const {params, data} = this.____getClassConfig();
return params?.[name] || data?.[name] || null;
},
forEach: function(executor) {
const keys = this.dataFunctions.getKeys();
for (const [index, key] of Help4.arrayEntries(keys)) {
executor.call(this, key, index, keys);
}
},
every: function(executor) {
const keys = this.dataFunctions.getKeys();
for (const [index, key] of Help4.arrayEntries(keys)) {
if (!executor.call(this, key, index, keys)) return false;
}
return true;
},
merge: function(instance, mergeFn) {
if (instance instanceof this.constructor) {
return mergeFn(this.dataFunctions.clone(), instance);
}
throw new Error('Use merge for same classes only!');
},
getTexts: function() {
const {TEXT_CONTENT_TYPE, TEXT_SET, TEXT_GET} = this.constructor;
const {texts = {}} = this.____getClassConfig();
const result = {};
for (const [key, name] of Object.entries(texts)) {
result[key] = {
name,
contentType: TEXT_CONTENT_TYPE[name],
set: TEXT_SET[name],
get: TEXT_GET[name]
};
}
return result;
},
}
})();