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-split.js | |
| parent | d9b7653700040bf25cb5681b5dc7021bc505d975 (diff) | |
added imthresh
Diffstat (limited to 'share/frontend/imthresh/lib/jsonform-split.js')
| -rw-r--r-- | share/frontend/imthresh/lib/jsonform-split.js | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/share/frontend/imthresh/lib/jsonform-split.js b/share/frontend/imthresh/lib/jsonform-split.js new file mode 100644 index 0000000..acc657a --- /dev/null +++ b/share/frontend/imthresh/lib/jsonform-split.js @@ -0,0 +1,320 @@ +/** + * @fileoverview The JSON Form "split" library exposes a "split" method + * that can be used to divide a JSON Form object into two disjoint + * JSON Form objects: + * - the first one includes the schema and layout of the form that + * contains the list of keys given as parameters as well as keys that + * cannot be separated from them (typically because they belong to the + * same array in the layout) + * - the second one includes the schema and layout of a form that does not + * contain the list of keys given as parameters. + * + * The options parameter lets one be explicit about whether it wants to include + * fields that are tightly coupled with the provided list of keys or not. + */ +/*global exports, _*/ + +(function (serverside, global, _) { + if (serverside && !_) { + _ = require('underscore'); + } + + /** + * Splits a JSON Form object into two autonomous JSON Form objects, + * one that includes the provided list of schema keys as well as keys + * that are tightly coupled to these keys, and the other that does not + * include these keys. + * + * The function operates on the "schema", "form", and "value" properties + * of the initial JSON Form object. It copies over the other properties + * to the resulting JSON Form objects. + * + * Note that the split function does not support "*" form definitions. The + * "form" property must be set in the provided in the provided JSON Form + * object. + * + * @function + * @param {Object} jsonform JSON Form object with a "schema" and "form" + * @param {Array(String)} keys Schema keys used to split the form. Each + * key must reference a schema key at the first level in the schema + * (in other words, the keys cannot include "." or "[]") + * @param {Object} options Split options. Set the "excludeCoupledKeys" flag + * not to include keys that are tightly coupled with the ones provided in + * the included part of the JSON Form object. + * @return {Object} An object with an "included" property whose value is + * the JSON Form object that includes the keys and an "excluded" property + * whose value is the JSON Form object that does not contain any of the + * keys. These objects may be empty. + */ + var split = function (jsonform, keys, options) { + options = options || {}; + keys = keys || []; + if (!jsonform || !jsonform.form) { + return { + included: {}, + excluded: {} + }; + } + + if (_.isString(keys)) { + keys = [keys]; + } + + // Prepare the object that will be returned + var result = { + included: { + schema: { + properties: {} + }, + form: [] + }, + excluded: { + schema: { + properties: {} + }, + form: [] + } + }; + + // Copy over properties such as "value" or "tpldata" that do not need + // to be split (note both forms will reference the same structures) + _.each(jsonform, function (value, key) { + if ((key !== 'schema') && (key !== 'form')) { + result.included[key] = value; + result.excluded[key] = value; + } + }); + + + /** + * Helper function that parses the given field and returns true if + * it references one of the keys to include directly. Note the function + * does not parse the potential children of the field and will thus + * return false even if the field actually references a key to include + * indirectly. + * + * @function + * @param {Object} formField The form field to parse + * @return {boolean} true when the field references one of the keys to + * include, false when not + */ + var formFieldReferencesKey = function (formField) { + var referencedKey = _.isString(formField) ? + formField : + formField.key; + if (!referencedKey) { + return false; + } + return _.include(keys, referencedKey) || + !!_.find(keys, function (key) { + return (referencedKey.indexOf(key + '.') === 0) || + (referencedKey.indexOf(key + '[]') === 0); + }); + }; + + + /** + * Helper function that parses the given field and returns true if + * it references a key that is not in the list of keys to include. + * Note the function does not parse the potential children of the field + * and will thus return false even if the field actually references a key + * to include indirectly. + * + * @function + * @param {Object} formField The form field to parse + * @return {boolean} true when the field references one of the keys to + * include, false when not + */ + var formFieldReferencesOtherKey = function (formField) { + var referencedKey = _.isString(formField) ? + formField : + formField.key; + if (!referencedKey) { + return false; + } + return !_.include(keys, referencedKey) && + !_.find(keys, function (key) { + return (referencedKey.indexOf(key + '.') === 0) || + (referencedKey.indexOf(key + '[]') === 0); + }); + }; + + + /** + * Helper function that parses the given field and returns true if + * it references one of the keys to include somehow (either directly + * or through one of its descendants). + * + * @function + * @param {Object} formField The form field to parse + * @return {boolean} true when the field references one of the keys to + * include, false when not + */ + var includeFormField = function (formField) { + return formFieldReferencesKey(formField) || + formField.items && !!_.some(formField.items, function (item) { + return includeFormField(item); + }); + }; + + + /** + * Helper function that parses the given field and returns true if + * it references a key that is not one of the keys to include somehow + * (either directly or through one of its descendants). + * + * @function + * @param {Object} formField The form field to parse + * @return {boolean} true when the field references one of the keys to + * include, false when not + */ + var excludeFormField = function (formField) { + return formFieldReferencesOtherKey(formField) || + formField.items && !!_.some(formField.items, function (item) { + return excludeFormField(item); + }); + }; + + + /** + * Converts the provided form field for inclusion in the included/excluded + * portion of the result. The function returns null if the field should not + * appear in the relevant part. + * + * Note the function is recursive. + * + * @function + * @param {Object} formField The form field to convert + * @param {string} splitPart The targeted result part, one of "included", + * "excluded", or "all". The "all" string is used in recursions to force + * the inclusion of the field even if it does not reference one of the + * provided keys. + * @param {Object} parentField Pointer to the form field parent. This + * parameter is used in recursions to preserve direct children of a + * "selectfieldset". + * @return {Object} The converted field. + */ + var convertFormField = function (formField, splitPart, parentField) { + var convertedField = null; + + var keepField = formField.root || + (splitPart === 'all') || + (parentField && parentField.key && + (parentField.type === 'selectfieldset')) || + (formField.type && formField.type === 'help'); + if (!keepField) { + keepField = (splitPart === 'included') && includeFormField(formField); + } + if (!keepField) { + keepField = (splitPart === 'excluded') && excludeFormField(formField); + if (keepField && !options.excludeCoupledKeys) { + keepField = !includeFormField(formField); + } + } + if (!keepField) { + return null; + } + + var childPart = splitPart; + if ((childPart === 'included') && + !options.excludeCoupledKeys && + !formField.root) { + childPart = 'all'; + } + + // Make a shallow copy of the field since we will preserve all of its + // properties (save perhaps "items") + convertedField = _.clone(formField); + + // Recurse through the descendants of the field + if (convertedField.items) { + convertedField.items = _.map(convertedField.items, function (field) { + return convertFormField(field, childPart, convertedField); + }); + convertedField.items = _.compact(convertedField.items); + } + return convertedField; + }; + + + /** + * Helper function that checks the given schema key definition + * and returns true when the definition is referenced in the given + * form field definition + * + * @function + * @param {Object} formField The form field to check + * @param {string} schemaKey The key to search in the form field + * @return {boolean} true if the form field references the key somehow, + * false otherwise. + */ + var includeSchemaKey = function (formField, schemaKey) { + if (!formField) return false; + if (!schemaKey) return false; + + if (_.isString(formField)) { + // Direct reference to a key in the schema + return (formField === schemaKey) || + (formField.indexOf(schemaKey + '.') === 0) || + (formField.indexOf(schemaKey + '[]') === 0); + } + + if (formField.key) { + if ((formField.key === schemaKey) || + (formField.key.indexOf(schemaKey + '.') === 0) || + (formField.key.indexOf(schemaKey + '[]') === 0) + ) { + return true; + } + } + + return !!_.some(formField.items, function (item) { + return includeSchemaKey(item, schemaKey); + }); + }; + + + // Prepare the included/excluded forms + var converted = null; + converted = convertFormField({ + items: jsonform.form, + root: true + }, 'included'); + if (converted) { + result.included.form = converted.items; + } + converted = convertFormField({ + items: jsonform.form, + root: true + }, 'excluded'); + if (converted) { + result.excluded.form = converted.items; + } + + // Split the schema into two schemas. + // (note that the "excluded" JSON Form object may contain keys that + // are never referenced in the initial JSON Form layout. That's normal) + var schemaProperties = jsonform.schema; + if (schemaProperties.properties) { + schemaProperties = schemaProperties.properties; + } + _.each(schemaProperties, function (schemaDefinition, schemaKey) { + if (_.some(result.included.form, function (formField) { + return includeSchemaKey(formField, schemaKey); + })) { + result.included.schema.properties[schemaKey] = schemaDefinition; + } + else { + result.excluded.schema.properties[schemaKey] = schemaDefinition; + } + }); + + return result; + }; + + global.JSONForm = global.JSONForm || {}; + global.JSONForm.split = split; + +})((typeof exports !== 'undefined'), + ((typeof exports !== 'undefined') ? exports : window), + ((typeof _ !== 'undefined') ? _ : null)); |
