123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /**
- * @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));
|