diff options
| author | yo mama <pepper@scannerjammer.com> | 2016-10-01 13:56:42 -0700 |
|---|---|---|
| committer | yo mama <pepper@scannerjammer.com> | 2016-10-01 13:56:42 -0700 |
| commit | 7f2f9bc05206afa66afe7d71d4f3484e39f40a34 (patch) | |
| tree | cb05348cf7387b817dea58080efd34ca26b8bdd2 /share/frontend/imthresh/lib/jsonform-defaults.js | |
| parent | d9b7653700040bf25cb5681b5dc7021bc505d975 (diff) | |
added imthresh
Diffstat (limited to 'share/frontend/imthresh/lib/jsonform-defaults.js')
| -rw-r--r-- | share/frontend/imthresh/lib/jsonform-defaults.js | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/share/frontend/imthresh/lib/jsonform-defaults.js b/share/frontend/imthresh/lib/jsonform-defaults.js new file mode 100644 index 0000000..fad62c1 --- /dev/null +++ b/share/frontend/imthresh/lib/jsonform-defaults.js @@ -0,0 +1,331 @@ +/** + * @fileoverview The JSON Form "defaults" library exposes a setDefaultValues + * method that extends the object passed as argument so that it includes + * values for all required fields of the JSON schema it is to follow that + * define a default value. + * + * The library is called to complete the configuration settings of a template in + * the Factory and to complete datasource settings. + * + * The library is useless if the settings have already been validated against the + * schema using the JSON schema validator (typically, provided the validator is + * loaded, submitting the form created from the schema will raise an error when + * required properties are missing). + * + * Note the library does not validate the created object, it merely sets missing + * values to the default values specified in the schema. All other values may + * be invalid. + * + * Nota Bene: + * - in data-joshfire, the runtime/nodejs/lib/jsonform-defaults.js file is a + * symbolic link to the jsonform submodule in deps/jsonform + * - in platform-joshfire, the server/public/js/libs/jsonform-defaults.js file + * is a symbolic link to the jsonform submodule in deps/jsonform + */ + +(function () { + // Establish the root object: + // that's "window" in the browser, "global" in node.js + var root = this; + + /** + * Sets default values, ensuring that fields defined as "required" in the + * schema appear in the object. If missing, the hierarchy that leads to + * a required key is automatically created. + * + * @function + * @param {Object} obj The object to complete with default values according + * to the schema + * @param {Object} schema The JSON schema that the object follows + * @param {boolean} includeOptionalValues Include default values for fields + * that are not "required" + * @param {boolean} skipFieldsWithoutDefaultValue Set flag not to include a + * generated empty default value for fields marked as "required" in the + * schema but that do not define a default value. + * @return {Object} The completed object (same instance as obj) + */ + var setDefaultValues = function (obj, schema, includeOptionalValues, skipFieldsWithoutDefaultValue) { + if (!obj || !schema) return obj; + if (!schema.properties) { + schema = { properties: schema }; + } + + // Inner function that parses the schema recursively to build a flat + // list of defaults + var defaults = {}; + var extractDefaultValues = function (schemaItem, path) { + var properties = null; + var child = null; + + if (!schemaItem || (schemaItem !== Object(schemaItem))) return null; + + if (schemaItem.required) { + // Item is required + if (schemaItem['default']) { + // Item defines a default value, let's use it, + // no need to continue in that case, we have the default value + // for the whole subtree starting at schemaItem. + defaults[path] = schemaItem['default']; + return; + } + else if (skipFieldsWithoutDefaultValue) { + // Required but no default value and caller explicitly asked not + // include such fields in the returned object. + } + else if ((schemaItem.type === 'object') || schemaItem.properties) { + // Item is a required object + defaults[path] = {}; + } + else if ((schemaItem.type === 'array') || schemaItem.items) { + // Item is a required array + defaults[path] = []; + } + else if (schemaItem.type === 'string') { + defaults[path] = ''; + } + else if ((schemaItem.type === 'number') || (schemaItem.type === 'integer')) { + defaults[path] = 0; + } + else if (schemaItem.type === 'boolean') { + defaults[path] = false; + } + else { + // Unknown type, use an empty object by default + defaults[path] = {}; + } + } + else if (schemaItem['default'] && includeOptionalValues) { + // Item is not required but defines a default value and the + // include optional values flag is set, so let's use it. + // No need to continue in that case, we have the default value + // for the whole subtree starting at schemaItem. + defaults[path] = schemaItem['default']; + return; + } + + // Parse schema item's properties recursively + properties = schemaItem.properties; + if (properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + extractDefaultValues(properties[key], path + '.' + key); + } + } + } + + // Parse schema item's children recursively + if (schemaItem.items) { + // Items may be a single item or an array composed of only one item + child = schemaItem.items; + if (_isArray(child)) { + child = child[0]; + } + + extractDefaultValues(child, path + '[]'); + } + }; + + // Build a flat list of default values + extractDefaultValues(schema, ''); + + // Ensure the object's default values are correctly set + for (var key in defaults) { + if (defaults.hasOwnProperty(key)) { + setObjKey(obj, key, defaults[key]); + } + } + }; + + + /** + * Retrieves the default value for the given key in the schema + * + * Levels in the path are separated by a dot. Array items are marked + * with []. For instance: + * foo.bar[].baz + * + * @function + * @param {Object} schema The schema to parse + * @param {String} key The path to the key whose default value we're + * looking for. each level is separated by a dot, and array items are + * flagged with [x]. + * @return {Object} The default value, null if not found. + */ + var getSchemaKeyDefaultValue = function(schema,key) { + var schemaKey = key + .replace(/\./g, '.properties.') + .replace(/\[.*\](\.|$)/g, '.items$1'); + var schemaDef = getObjKey(schema, schemaKey); + if (schemaDef) return schemaDef['default']; + return null; + }; + + /** + * Retrieves the key identified by a path selector in the structured object. + * + * Levels in the path are separated by a dot. Array items are marked + * with []. For instance: + * foo.bar[].baz + * + * @function + * @param {Object} obj The object to parse + * @param {String} key The path to the key whose default value we're + * looking for. each level is separated by a dot, and array items are + * flagged with [x]. + * @return {Object} The key definition, null if not found. + */ + var getObjKey = function (obj, key) { + var innerobj = obj; + var keyparts = key.split('.'); + var subkey = null; + var arrayMatch = null; + var reArraySingle = /\[([0-9]*)\](?:\.|$)/; + + for (var i = 0; i < keyparts.length; i++) { + if (typeof innerobj !== 'object') return null; + subkey = keyparts[i]; + arrayMatch = subkey.match(reArraySingle); + if (arrayMatch) { + // Subkey is part of an array + subkey = subkey.replace(reArraySingle, ''); + if (!_isArray(innerobj[subkey])) { + return null; + } + innerobj = innerobj[subkey][parseInt(arrayMatch[1], 10)]; + } + else { + innerobj = innerobj[subkey]; + } + } + + return innerobj; + }; + + + /** + * Sets the key identified by a path selector to the given value. + * + * Levels in the path are separated by a dot. Array items are marked + * with []. For instance: + * foo.bar[].baz + * + * The hierarchy is automatically created if it does not exist yet. + * + * Default values are added to all array items. Array items are not + * automatically created if they do not exist (in particular, the + * minItems constraint is not enforced) + * + * @function + * @param {Object} obj The object to build + * @param {String} key The path to the key to set where each level + * is separated by a dot, and array items are flagged with [x]. + * @param {Object} value The value to set, may be of any type. + */ + var setObjKey = function (obj, key, value) { + var keyparts = key.split('.'); + + // Recursive version of setObjKey + var recSetObjKey = function (obj, keyparts, value) { + var arrayMatch = null; + var reArray = /\[([0-9]*)\]$/; + var subkey = keyparts.shift(); + var idx = 0; + + if (keyparts.length > 0) { + // Not the end yet, build the hierarchy + arrayMatch = subkey.match(reArray); + if (arrayMatch) { + // Subkey is part of an array, check all existing array items + // TODO: review that! Only create the right item!!! + subkey = subkey.replace(reArray, ''); + if (!_isArray(obj[subkey])) { + obj[subkey] = []; + } + obj = obj[subkey]; + if (arrayMatch[1] !== '') { + idx = parseInt(arrayMatch[1], 10); + if (!obj[idx]) { + obj[idx] = {}; + } + recSetObjKey(obj[idx], keyparts, value); + } + else { + for (var k = 0; k < obj.length; k++) { + recSetObjKey(obj[k], keyparts, value); + } + } + return; + } + else { + // "Normal" subkey + if (typeof obj[subkey] !== 'object') { + obj[subkey] = {}; + } + obj = obj[subkey]; + recSetObjKey(obj, keyparts, value); + } + } + else { + // Last key, time to set the value, unless already defined + arrayMatch = subkey.match(reArray); + if (arrayMatch) { + subkey = subkey.replace(reArray, ''); + if (!_isArray(obj[subkey])) { + obj[subkey] = []; + } + idx = parseInt(arrayMatch[1], 10); + if (!obj[subkey][idx]) { + obj[subkey][idx] = value; + } + } + else if (!obj[subkey]) { + obj[subkey] = value; + } + } + }; + + // Skip first item if empty (key starts with a '.') + if (!keyparts[0]) { + keyparts.shift(); + } + recSetObjKey(obj, keyparts, value); + }; + + // Taken from Underscore.js (not included to save bytes) + var _isArray = Array.isArray || function (obj) { + return Object.prototype.toString.call(obj) == '[object Array]'; + }; + + + // Export the code as: + // 1. an AMD module (the "define" method exists in that case), or + // 2. a node.js module ("module.exports" is defined in that case), or + // 3. a global JSONForm object (using "root") + if (typeof define !== 'undefined') { + // AMD module + define([], function () { + return { + setDefaultValues: setDefaultValues, + setObjKey: setObjKey, + getSchemaKeyDefaultValue: getSchemaKeyDefaultValue + }; + }); + } + else if ((typeof module !== 'undefined') && module.exports) { + // Node.js module + module.exports = { + setDefaultValues: setDefaultValues, + setObjKey: setObjKey, + getSchemaKeyDefaultValue: getSchemaKeyDefaultValue + }; + } + else { + // Export the function to the global context, using a "string" for + // Google Closure Compiler "advanced" mode + // (not sure why it's needed, done by Underscore) + root['JSONForm'] = root['JSONForm'] || {}; + root['JSONForm'].setDefaultValues = setDefaultValues; + root['JSONForm'].setObjKey = setObjKey; + root['JSONForm'].getSchemaKeyDefaultValue = getSchemaKeyDefaultValue; + } +})();
\ No newline at end of file |
