diff --git a/build/index.js b/build/index.js index 2c678cf..a9dced8 100644 --- a/build/index.js +++ b/build/index.js @@ -2911,7 +2911,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _mix /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return FormProcessor; });\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! expr-eval */ \"./node_modules/expr-eval/dist/bundle.js\");\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(expr_eval__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! lodash/clone */ \"./node_modules/lodash/clone.js\");\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lodash_clone__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! lodash/merge */ \"./node_modules/lodash/merge.js\");\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! lodash/defaultsDeep */ \"./node_modules/lodash/defaultsDeep.js\");\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! lodash/map */ \"./node_modules/lodash/map.js\");\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(lodash_map__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! lodash/reduce */ \"./node_modules/lodash/reduce.js\");\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(lodash_reduce__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! lodash/has */ \"./node_modules/lodash/has.js\");\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(lodash_has__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/get */ \"./node_modules/lodash/get.js\");\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_get__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! lodash/set */ \"./node_modules/lodash/set.js\");\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(lodash_set__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! lodash/pull */ \"./node_modules/lodash/pull.js\");\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(lodash_pull__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! lodash/defaultTo */ \"./node_modules/lodash/defaultTo.js\");\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! lodash/difference */ \"./node_modules/lodash/difference.js\");\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(lodash_difference__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! lodash/intersection */ \"./node_modules/lodash/intersection.js\");\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(lodash_intersection__WEBPACK_IMPORTED_MODULE_12__);\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lodash/isPlainObject */ \"./node_modules/lodash/isPlainObject.js\");\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__);\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! lodash/toNumber */ \"./node_modules/lodash/toNumber.js\");\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__);\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! lodash/zipObject */ \"./node_modules/lodash/zipObject.js\");\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__);\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! lodash/sortBy */ \"./node_modules/lodash/sortBy.js\");\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__);\n/* harmony import */ var _mixins_util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../mixins/util */ \"./src/mixins/util.js\");\n/* harmony import */ var _functions_sum__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../functions/sum */ \"./src/functions/sum.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! lodash/sumBy */ \"./node_modules/lodash/sumBy.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default = /*#__PURE__*/__webpack_require__.n(lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__);\n/* harmony import */ var _functions_multiply__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../functions/multiply */ \"./src/functions/multiply.js\");\n/* harmony import */ var _functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../functions/buildDictionary */ \"./src/functions/buildDictionary.js\");\n/* harmony import */ var _functions_buildTree__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../functions/buildTree */ \"./src/functions/buildTree.js\");\n/* harmony import */ var _functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../functions/traverseTree */ \"./src/functions/traverseTree.js\");\n/* harmony import */ var _functions_splitPath__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../functions/splitPath */ \"./src/functions/splitPath.js\");\n/* harmony import */ var _functions_joinPath__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../functions/joinPath */ \"./src/functions/joinPath.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! flat */ \"./node_modules/flat/index.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(flat__WEBPACK_IMPORTED_MODULE_26__);\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! deep-object-diff */ \"./node_modules/deep-object-diff/dist/index.js\");\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27___default = /*#__PURE__*/__webpack_require__.n(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); }\n\nfunction _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar UP = -1;\nvar BOTH = 0;\nvar DOWN = 1;\n/**\n * Pragma form.\n *\n * Expands field lists a dictionary and tree. Processes field expressions from\n * state data.\n *\n * TODO: Rename to Form?\n *\n * @class FormProcessor\n */\n\nvar FormProcessor =\n/*#__PURE__*/\nfunction () {\n /**\n * Create a new property processor.\n *\n * @constructor\n * @param {Field[]} [fields=[]] - Initial form fields.\n * @param {Object.} [functions={}] - Functions to make available for field expressions.\n * @param {Object.} [inputOptions={}] - Default input options keyed by input type.\n */\n function FormProcessor() {\n var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var functions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var inputOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n _classCallCheck(this, FormProcessor);\n\n /**\n * Typecasting functions for each field type.\n *\n * TODO: Strong casting functions\n *\n * @type {Object.}\n */\n this.casts = {\n 'string': function string(f, v) {\n return v == null ? '' : '' + v;\n },\n 'number': function number(f, v) {\n return lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default()(_mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].clamp(v, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.min'), lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.max')));\n },\n 'boolean': function boolean(f, v) {\n return !!v;\n }\n };\n /**\n * Default property values for each field type.\n *\n * @type {Object}\n */\n\n this.defaults = {};\n this.setDefaults({\n '*': {\n type: 'number',\n visible: true\n },\n 'virtual': {\n visible: false,\n virtual: true,\n omit: true\n },\n 'string': {\n input: 'string',\n default: ''\n },\n 'number': {\n input: 'number',\n default: 0,\n options: {\n min: -100,\n max: 100,\n step: 1\n }\n },\n 'boolean': {\n input: 'boolean',\n default: false\n },\n 'selection': {\n input: 'selection',\n options: {\n options: {}\n }\n },\n 'section': {\n input: 'section'\n },\n 'group': {\n input: 'group'\n },\n 'list': {\n input: 'list'\n },\n 'list-item': {\n input: 'list-item'\n },\n 'table': {\n input: 'pragma-table'\n }\n });\n /**\n * Default input options for each input type.\n *\n * @type {Object.}\n */\n\n this.inputOptions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, inputOptions);\n /**\n * Expression functions.\n *\n * @type {Object.}\n */\n\n this.functions = {};\n /**\n * Expression parser.\n *\n * @type {Parser}\n */\n\n this.parser = new expr_eval__WEBPACK_IMPORTED_MODULE_0___default.a.Parser({\n operators: {\n in: true\n }\n }); // Set the functions\n\n this.addFunctions(lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({\n concat: function concat() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return args.join('');\n },\n keys: Object.keys,\n multiply: _functions_multiply__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n sum: _functions_sum__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n sumBy: lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default.a,\n map: lodash_map__WEBPACK_IMPORTED_MODULE_4___default.a,\n reduce: lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default.a\n }, functions));\n /**\n * The set of form fields.\n *\n * @type {Field[]}\n */\n\n this.fields = [];\n /**\n * Fields keyed by path.\n *\n * @type {FieldDictionary}\n */\n\n this.dictionary = {};\n /**\n * The root node of the field tree.\n *\n * @type {Field}\n */\n\n this.tree = {}; // Set the form fields\n\n this.setFields(fields);\n /**\n * Value cache for each field.\n *\n * @type {Object.}\n */\n\n this.valueCache = {};\n /**\n * Field expression cache keyed by path.\n *\n * @type {Object.}\n */\n\n this.expressionCache = {};\n /**\n * Field update dependencies keyed by path.\n *\n * @type {Object.}\n */\n\n this.fieldDependencies = {};\n }\n /**\n * Check whether a field exists at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to check.\n * @return {boolean}\n */\n\n\n _createClass(FormProcessor, [{\n key: \"hasField\",\n value: function hasField(path) {\n return lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(this.dictionary, path);\n }\n /**\n * Get the field at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to get.\n * @return {Field}\n */\n\n }, {\n key: \"getField\",\n value: function getField(path) {\n return this.dictionary[path];\n }\n /**\n * Get the parent field of the field at the given path.\n *\n * @protected\n * @param {Field} field\n * @return {Field|null}\n */\n\n }, {\n key: \"getFieldParent\",\n value: function getFieldParent(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.parent);\n }\n /**\n * Get the ancestors of a field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]}\n */\n\n }, {\n key: \"getFieldAncestors\",\n value: function getFieldAncestors(field) {\n var ancestors = [];\n\n while (field.hasOwnProperty('parent') && this.hasField(field.parent)) {\n field = this.getFieldParent(field);\n ancestors.push(field);\n }\n\n return ancestors;\n }\n /**\n * Get the current value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the value of.\n * @param {*} [data={}] - Optional data to read current values from.\n * @param {*} [value] - Optional current value.\n * @return {*} The current value of the field.\n */\n\n }, {\n key: \"getFieldValue\",\n value: function getFieldValue(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n\n if (!field) {\n return value;\n }\n\n value = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path)); // Merge default values if specified\n\n if (field.merge) {\n if (lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default()(field.default)) {\n return lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, field.default, field.value, value);\n } // TODO: Handle arrays\n\n } // Otherwise use the first defined value\n\n\n return lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n }\n /**\n * Get the default value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the default value of.\n * @return {*} The default value of the field.\n */\n\n }, {\n key: \"getFieldDefaultValue\",\n value: function getFieldDefaultValue(field) {\n if (!field) {\n return null;\n }\n\n return field.default;\n }\n /**\n * Get the template field that a field should a extend.\n *\n * @param {Field} field - The field to get the template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldTemplate\",\n value: function getFieldTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.extends);\n }\n /**\n * Get the keys of the field's children.\n *\n * @protected\n * @param {Field} field - The field to get the child keys of.\n * @return {array} The keys of the field's children.\n */\n\n }, {\n key: \"getFieldChildrenKeys\",\n value: function getFieldChildrenKeys(field) {\n var i,\n keys = [],\n children = field.children || [];\n\n for (i = 0; i < children.length; i++) {\n keys.push(children[i].pathFragment);\n }\n\n return keys;\n }\n /**\n * Get the template that a field's children should extend.\n *\n * @protected\n * @param {Field} field - The field to get the child template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldChildrenTemplate\",\n value: function getFieldChildrenTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.template);\n }\n /**\n * Get the fields dependent upon the given field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]} The dependent fields\n */\n\n }, {\n key: \"getFieldDependencies\",\n value: function getFieldDependencies(field) {\n var dependencies = this.fieldDependencies[field.path]; // Skip if the field has no dependencies\n\n if (!dependencies || !dependencies.length) {\n return [];\n }\n\n var fields = [];\n\n for (var i = 0; i < dependencies.length; i++) {\n var dependency = this.getField(dependencies[i]);\n\n if (!dependency) {\n continue;\n }\n\n fields.push(dependency);\n }\n\n return fields;\n }\n /**\n * Get the current value of a field.\n *\n * Falls back to default values as appropriate.\n *\n * @public\n * @param {string} path - The path to the field.\n * @return {*} The value of the field\n */\n\n }, {\n key: \"getValue\",\n value: function getValue(path) {\n return this.getFieldValue(this.getField(path));\n }\n /**\n * Cast a value based on the property it belongs to.\n *\n * @public\n * @param {Field} field\n * @param {*} value\n */\n\n }, {\n key: \"castValue\",\n value: function castValue(field, value) {\n if (!field) return value;\n if (!this.casts[field.type]) return value; // if (Array.isArray(value))\n // \treturn value.map(this.casts[field.type]);\n\n value = this.casts[field.type](field, value);\n return value;\n }\n /**\n * Derive a field's value from some data.\n *\n * @protected\n * @param {string} path - The path of the field to derive a value for.\n * @param {Object} data - The data to derive values from.\n * @return {*} The derived value.\n */\n\n }, {\n key: \"deriveValue\",\n value: function deriveValue(path, data) {\n // Return from the value cache if a value is set\n if (this.valueCache.hasOwnProperty(path)) {\n return this.valueCache[path];\n }\n\n var field = this.getField(path);\n var value = this.getFieldValue(field, data); // Return the raw value if there's no such field\n\n if (!field) {\n return value;\n } // Cast the value\n\n\n value = this.castValue(field, value); // Evaluate the field's expression\n\n value = this.evaluateFieldExpression(field, data, value); // Fall back to defaults\n //value = defaultTo(value, defaultTo(field.default, null));\n\n value = this.getFieldValue(field, data, value); // Update the value cache\n\n this.valueCache[path] = value;\n return value;\n }\n /**\n * Build a field's expression.\n *\n * @param {Field} field - The field to build an expression for.\n * @return {Expression} The built expression.\n */\n\n }, {\n key: \"buildFieldExpression\",\n value: function buildFieldExpression(field) {\n if (field.expression == null || typeof field.expression !== 'string') {\n return null;\n } // Use the cached expression if one is available\n\n\n if (this.expressionCache[field.path]) {\n return this.expressionCache[field.path];\n } // Build the initial expression\n\n\n var expression;\n\n try {\n expression = this.parser.parse(field.expression);\n } catch (error) {\n console.error(\"Error parsing expression for field '\".concat(field.path, \"': \").concat(error.message));\n return null;\n } // Substitute contextual variables\n\n\n var substitutions = {\n $parent: field.parent\n };\n\n for (var s in substitutions) {\n try {\n expression = expression.substitute(s, substitutions[s]);\n } catch (error) {\n console.error(\"Error substituting expression variable '\".concat(s, \"' for field '\").concat(field.path, \": \").concat(error.message));\n return null;\n }\n }\n\n this.expressionCache[field.path] = expression;\n return expression;\n }\n /**\n * Evaluate a field's value from its expression.\n *\n * Causes the evaluation of any field dependencies as a result.\n *\n * TODO: Evaluate (and cache) expressions for other field properties! :D\n *\n * @param {Field} field - The field to compute the value of.\n * @param {Object} data - The data to derive values from.\n * @param {*} [value] - The current value of the field.\n * @return {*} The computed value of the field's expression.\n */\n\n }, {\n key: \"evaluateFieldExpression\",\n value: function evaluateFieldExpression(field, data, value) {\n var _this = this;\n\n value = this.getFieldValue(field, data, value); // Parse the expression\n\n var expression = this.buildFieldExpression(field);\n\n if (!expression) {\n return value;\n } // TODO: Extract deriving variables and building contextual functions\n // let variables = buildExpressionContext(field, data, expression, value)?\n // Contextual functions should still update the same \"variables\" reference\n // Derive values for the variables in the expression\n\n\n var variables = expression.variables({\n withMembers: true\n });\n var values = {\n $this: field,\n $value: value\n };\n\n for (var v = 0; v < variables.length; v++) {\n var variable = variables[v];\n if (lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(values, variable)) continue;\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(values, variable, this.deriveValue(variable, data));\n } // Build contextual functions\n\n\n values = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(values, {\n field: function field(path) {\n variables.push(path);\n return _this.getField(path);\n },\n value: function value(path) {\n // Add the path to the list of expression variables\n variables.push(path); // Derive the value for the expression\n\n return _this.deriveValue(path, data);\n }\n }); // Evaluate the expression\n\n try {\n value = expression.evaluate(values);\n } catch (error) {\n console.log('evaluateFieldExpression', field, data, value);\n console.error(\"Error evaluating expression for field '\".concat(field.path, \"': \").concat(error.message));\n } //console.log('evaluateFieldExpression', field.path, expression.toString(), variables, values, value);\n //console.log('evaluateFieldExpression expression', expression);\n // Update the map of field update dependencies\n // TODO: Exclude contextual variables\n // TODO: Move this to an earlier processing step that evaluates the\n // expression with spy functions\n\n\n for (var _v = 0; _v < variables.length; _v++) {\n var _variable = variables[_v];\n this.fieldDependencies[_variable] = this.fieldDependencies[_variable] || [];\n\n if (this.fieldDependencies[_variable].indexOf(field.path) < 0) {\n this.fieldDependencies[_variable].push(field.path);\n }\n }\n\n return value;\n }\n /**\n * Prepare fields from a set of field descriptions.\n *\n * Ascertain's the parent path and path fragment (key) of each field.\n *\n * TODO: Field class, FieldDescription typedef.\n *\n * @protected\n * @param {Field[]} fields - The field description.\n * @returns {Field[]} The given fields prepared with pathFragment and parent properties.\n */\n\n }, {\n key: \"prepareFields\",\n value: function prepareFields(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field, pathFragment, parentPath;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Ascertain a parent path and path fragment\n\n var _splitPath = Object(_functions_splitPath__WEBPACK_IMPORTED_MODULE_24__[\"default\"])(field.path);\n\n var _splitPath2 = _slicedToArray(_splitPath, 2);\n\n parentPath = _splitPath2[0];\n pathFragment = _splitPath2[1];\n field.pathFragment = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.pathFragment, pathFragment);\n field.parent = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.parent, parentPath);\n }\n\n return fields;\n }\n /**\n * Set the form's fields.\n *\n * TODO: Creates, updates and removes fields accordingly.\n *\n * @public\n * @param {Field[]} fields\n */\n\n }, {\n key: \"setFields\",\n value: function setFields(fields) {\n // Prepare the fields\n this.fields = this.prepareFields(fields); // TODO: Build dictionary from given fields, compare with current fields\n //this.dictionary = this.buildDictionary(this.fields);\n\n this.dictionary = this.updateDictionary(this.fields); // Compose the fields into a tree\n\n this.tree = this.buildTree(this.dictionary); // Clear all caches\n\n this.valueCache = {};\n this.expressionCache = {};\n this.fieldDependencies = {};\n }\n /**\n * Add a form field.\n *\n * @param {Field} field\n */\n\n }, {\n key: \"addField\",\n value: function addField(field) {\n var parent = this.getFieldParent(field);\n\n if (!parent) {\n console.warn(\"Could not set field \".concat(field.path, \" - its parent does not exist\"));\n return;\n }\n\n if (!parent.children) {\n parent.children = [];\n }\n\n if (!parent.children.includes(field)) {\n parent.children.push(field);\n }\n\n this.dictionary[field.path] = field;\n }\n /**\n * Update a property with the given value.\n *\n * @public\n * @param {Object} data - The data to update.\n * @param {string} path - The path of the field to update.\n * @param {*} value - The value to set.\n * @return {*} The updated value\n */\n\n }, {\n key: \"setValue\",\n value: function setValue(data, path, value) {\n var field = this.getField(path); // Update the value if one is given\n\n if (value !== undefined) {\n if (field) {\n field.value = value;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, value);\n } // Update the field at this path\n\n\n this.updatePath(path, data); // Get the updated value\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path);\n }\n /**\n * Clear the value cache.\n *\n * Optionally accepts a path to clear.\n *\n * @param {string} path\n */\n\n }, {\n key: \"clearValueCache\",\n value: function clearValueCache() {\n var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n\n // Just empty the entire cache if there's no path is or there's no field\n // at the given path\n if (!path || !this.getField(path)) {\n this.valueCache = {};\n return;\n }\n\n var i,\n field = this.getField(path); // Clear cached values of child fields iteratively\n\n var child,\n children = field.children,\n nextChildren = [];\n\n while (children && children.length) {\n nextChildren = [];\n\n for (i = 0; i < children.length; i++) {\n child = children[i];\n delete this.valueCache[child.path];\n\n if (child.children) {\n nextChildren = nextChildren.concat(child.children);\n }\n }\n\n children = nextChildren;\n } // Clear the cached value for this field\n\n\n delete this.valueCache[field.path]; // Clear cached values of parent fields iteratively\n\n var ancestors = this.getFieldAncestors(field);\n\n for (i = 0; i < ancestors.length; i++) {\n delete this.valueCache[ancestors[i].path];\n }\n }\n /**\n * Derive a property's name from its path.\n *\n * @protected\n * @param {Field} field\n * @return {string} The derived name\n */\n\n }, {\n key: \"deriveName\",\n value: function deriveName(field) {\n var path = field.path;\n var lastDotIndex = path.lastIndexOf('.');\n return _mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].sentenceCase(path.substring(lastDotIndex + 1));\n }\n /**\n * Set the default field properties for each type.\n *\n * @param {Object.} fieldProperties - The default field properties, keyed by field type.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(fieldProperties) {\n this.defaults = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.defaults, fieldProperties);\n }\n /**\n * Apply the form's default properties to the given fields.\n *\n * Fills in default values, derives default names.\n *\n * @protected\n * @param {Field[]} fields - The fields to apply default values to.\n * @returns {Field[]}\n */\n\n }, {\n key: \"applyDefaults\",\n value: function applyDefaults(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Derive a name\n\n if (field.name === undefined) {\n field.name = this.deriveName(field);\n } // Apply global defaults\n\n\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults['*']); // Apply type-specific defaults\n\n if (this.defaults[field.type]) {\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults[field.type]);\n } // Apply default input options\n\n\n if (this.inputOptions[field.input]) {\n field.options = field.options || {};\n field.options = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field.options, this.inputOptions[field.input]);\n } // Disable the field implicitly if it has an expression\n\n\n if (!field.hasOwnProperty('disabled')) {\n field.disabled = !!field.expression;\n }\n }\n\n return fields;\n }\n /**\n * Add to the form's functions.\n *\n * @param {Object.} functions - Functions to add, keyed by name.\n */\n\n }, {\n key: \"addFunctions\",\n value: function addFunctions(functions) {\n // Add the functions to the form\n this.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.functions, functions); // Add the functions to the expression parser\n\n this.parser.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.parser.functions, this.functions); // Clear the expression cache\n\n this.expressionCache = {};\n }\n /**\n * Update the form using the given data.\n *\n * @public\n * @param {Object} [data] - The data to update with.\n */\n\n }, {\n key: \"update\",\n value: function update(data) {\n // Update the value of every field\n this.updatePath('', data); // TODO: Diff any *paths* that changed and update those\n // i.e. implement diffFieldDataPaths()\n\n console.time('buildData');\n var formData = this.buildData(this.tree);\n console.timeEnd('buildData');\n console.log(formData);\n console.time('diff');\n var diff = Object(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__[\"detailedDiff\"])(formData, data);\n console.timeEnd('diff');\n console.log(diff);\n console.time('flatten');\n var addedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.added);\n var updatedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.updated);\n var deletedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.deleted);\n console.timeEnd('flatten');\n console.log(addedPaths, updatedPaths, deletedPaths); //let path;\n // for (path in addedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // for (path in updatedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n //\n // for (path in deletedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n\n console.time('traverseTree');\n Object(_functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__[\"default\"])(this.tree, function (field) {// Pre-order\n }, function (field) {\n // Post-order\n console.log(field.path);\n });\n console.timeEnd('traverseTree');\n }\n /**\n * Update the field at the given path.\n *\n * @param {string} path - The path of the field to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updatePath\",\n value: function updatePath(path, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n this.updateField(this.getField(path), data, direction);\n }\n /**\n * Update the given fields with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field[]} fields - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateFields\",\n value: function updateFields(fields, data, direction) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.updateField(fields[i], data, direction);\n }\n }\n /**\n * Update the given field with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field} field - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateField\",\n value: function updateField(field, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n\n if (!field) {\n return;\n } //console.log('updateField()', field.path);\n\n\n console.count('updateField()');\n this.clearValueCache(field.path); // Update the field's children\n\n if (direction >= 0) {\n // TODO: This would be a good spot for an event to fire to allow plugins\n // (like inheritance) to intercept child update behaviour\n this.updateFieldInheritance(field, data);\n\n if (!field.omit) {\n this.updateFields(field.children, data);\n }\n } // Apply default values\n\n\n this.applyDefaults([field]); // Update the state's value\n\n this.updateDataValue(field, data); // Update the field's value (and update all fields dependent on this one)\n\n this.updateFieldValue(field, data, direction <= 0);\n }\n /**\n * Update data from the given field.\n *\n * @protected\n * @param {Field} field - The field to update with.\n * @param {Object} data - The data to update.\n * @return {Object} The updated data.\n */\n\n }, {\n key: \"updateDataValue\",\n value: function updateDataValue(field, data) {\n if (!field || field.omit || field.virtual) {\n return data;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, this.deriveValue(field.path, data));\n return data;\n }\n /**\n * Update a field using the given data.\n *\n * @protected\n * @param {Field} field - The field to update.\n * @param {Object} data - The data to update with.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldValue\",\n value: function updateFieldValue(field, data) {\n var updateParents = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!field) {\n return;\n } // Update the field value\n\n\n field.value = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path); // Update all fields dependent on this one\n\n this.updateFieldDependencies(field, data, updateParents);\n }\n /**\n * Update fields that are dependent upon the value of the given field.\n *\n * @protected\n * @param {Field} field - The field to update dependencies of.\n * @param {Object} data - The data to update from.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldDependencies\",\n value: function updateFieldDependencies(field, data, updateParents) {\n // Recursively update parent field values\n if (updateParents) {\n this.updateFieldAncestors(field, data);\n } // Update fields listed as dependencies\n\n\n this.updateFields(this.getFieldDependencies(field), data);\n }\n /**\n * Update the ancestors of the given field.\n *\n * @protected\n * @param {Field} field - The field to update parents of.\n * @param {Object} data - The data to update with.\n */\n\n }, {\n key: \"updateFieldAncestors\",\n value: function updateFieldAncestors(field, data) {\n var i,\n parents = this.getFieldAncestors(field);\n\n for (i = 0; i < parents.length; i++) {\n this.updateFieldValue(parents[i], data);\n }\n }\n /**\n * Update a field's inheritance.\n *\n * Ensures that inherited child fields exist.\n *\n * @param {Field} field - The field to update the inheritance of.\n * @param {Object} data - The date to update with.\n */\n\n }, {\n key: \"updateFieldInheritance\",\n value: function updateFieldInheritance(field, data) {\n // Update child template fields\n this.updateTemplateFields(field, data); // Inherit the field's template\n\n this.inheritTemplate(field, data);\n }\n /**\n * Get the keys of child fields that don't exist in the given data.\n *\n * Retrieves the new keys, existing keys and old keys of a field compared\n * to its data.\n *\n * @protected\n * @param {Field} field - The field with a template.\n * @param {Object} data - The data to diff against.\n * @return array [newPaths[], existingPaths[], oldPaths[]]\n */\n\n }, {\n key: \"diffFieldDataKeys\",\n value: function diffFieldDataKeys(field, data) {\n // Grab the data keys and child field keys\n var childData = this.getFieldValue(field, data);\n var childDataKeys = childData ? Object.keys(childData) : [];\n var childFieldKeys = this.getFieldChildrenKeys(field); // Keys in data that aren't in fields\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childDataKeys, childFieldKeys); // Keys in data and fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(childDataKeys, childFieldKeys); // Keys in fields that aren't in data\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childFieldKeys, childDataKeys); // console.log('diffTemplateFieldKeys()', field.path, 'newKeys', newKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'existingKeys', existingKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'oldKeys', oldKeys);\n\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Unravel all templates into fields for the given field and data.\n *\n * TODO: Try to merge this into inheritTemplate(), or extract a method\n * that can handle both cases, like updateFieldChildrenInheritance().\n *\n * @protected\n * @param {Field} field - The field to update template fields for.\n * @param {Object} [data] - The data used to unravel field templates.\n */\n\n }, {\n key: \"updateTemplateFields\",\n value: function updateTemplateFields(field, data) {\n if (!field) {\n return;\n }\n\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return;\n }\n\n var dictionary = this.dictionary,\n i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Find child fields that need to be added, updated or removed\n\n var _this$diffFieldDataKe = this.diffFieldDataKeys(field, data),\n _this$diffFieldDataKe2 = _slicedToArray(_this$diffFieldDataKe, 3),\n newKeys = _this$diffFieldDataKe2[0],\n existingKeys = _this$diffFieldDataKe2[1],\n oldKeys = _this$diffFieldDataKe2[2];\n\n var existingFieldKeys = this.getFieldChildrenKeys(field);\n var fixedKeys = field.fixed || []; // TODO: Extract to... diffFixedFieldDataKeys()...?\n // Remove fixed keys from the keys that need removing\n\n oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(oldKeys, fixedKeys); // Add the existent fixed keys to the keys that need updating\n\n existingKeys = existingKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(fixedKeys, existingFieldKeys), existingKeys, newKeys)); // Add the non-existent fixed keys to the keys that need creating\n\n newKeys = newKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(fixedKeys, existingFieldKeys, newKeys)); //console.log(field.path, newKeys, existingKeys, oldKeys, fixedKeys, existingFieldKeys);\n // Remove old fields\n\n for (i = 0; i < oldKeys.length; i++) {\n key = oldKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n this.removeField(this.getField(path));\n } // Update existing fields\n\n\n for (i = 0; i < existingKeys.length; i++) {\n key = existingKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() existingField', field.path, path);\n // Ensure the existing field has the correct template\n\n existingField = this.getField(path);\n existingField.extends = template.path;\n } // Build new fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field);\n\n for (i = 0; i < newKeys.length; i++) {\n key = newKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() newField', path, value[key]);\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: template.path\n };\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields);\n }\n\n this.updateDictionary(newFields);\n }\n /**\n * Diff the keys of the first field's children with those of the second\n * field's children.\n *\n * Finds keys of the first that aren't of the second, keys that are of both,\n * and keys of the second that aren't of the first.\n *\n * @protected\n * @param {Field} firstField\n * @param {Field} secondField\n * @returns {array} [newKeys, existingKeys, oldKeys]\n */\n\n }, {\n key: \"diffFieldChildrenKeys\",\n value: function diffFieldChildrenKeys(firstField, secondField) {\n var firstFieldKeys = this.getFieldChildrenKeys(firstField);\n var secondFieldKeys = this.getFieldChildrenKeys(secondField); // Child keys of the first field that aren't of the second field\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(firstFieldKeys, secondFieldKeys); // Keys in the children of both fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(firstFieldKeys, secondFieldKeys); // Child keys of the second field that aren't of the first\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(secondFieldKeys, firstFieldKeys);\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Apply a field's inheritance.\n *\n * Ensures that a field inherits from its base field.\n *\n * @param {Field} field - The inheriting field.\n * @param {Object} data - The data to update with.\n * @return {Field}\n */\n\n }, {\n key: \"inheritTemplate\",\n value: function inheritTemplate(field, data) {\n if (!field) {\n return field;\n }\n\n var template = this.getFieldTemplate(field); // Skip fields without a template\n\n if (!template) {\n return field;\n } // Inherit the template\n\n\n if (field.extended !== template.path) {\n var templateClone = lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(template); // We don't want to inherit children, nor do we support more than a\n // single layer of inheritance\n\n delete templateClone.children;\n delete templateClone.template; // if (field.path === 'skills.list.test.ability') {\n // \tconsole.log('skills.list.test.ability', clone(field), templateClone);\n // }\n // Merge sandwich to retain original values\n\n field = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(field, templateClone, lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(field));\n\n if (field.name == null) {\n field.name = this.deriveName(field);\n }\n\n field.extended = template.path;\n this.prepareFields([field]);\n } //console.log('inheritTemplate()', field.path, template.path);\n // Update child field inheritance\n\n\n var i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Diff field child keys and template child keys\n\n var _this$diffFieldChildr = this.diffFieldChildrenKeys(template, field),\n _this$diffFieldChildr2 = _slicedToArray(_this$diffFieldChildr, 2),\n newKeys = _this$diffFieldChildr2[0],\n existingKeys = _this$diffFieldChildr2[1]; // Update existing template fields\n\n\n for (var _i2 = 0; _i2 < existingKeys.length; _i2++) {\n key = existingKeys[_i2];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n existingField = this.getField(path); //console.log('inheritTemplate() existingField', path, existingField);\n\n existingField.extends = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key);\n } // Build new template fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field); //console.log('inheritTemplate()', field.path, value);\n\n for (var _i3 = 0; _i3 < newKeys.length; _i3++) {\n key = newKeys[_i3];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('inherit newKey', path, value[key]);\n // Build the new field\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key)\n }; // Propagate values\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n // TODO: Extract addFieldChildren(field, children)\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields); // We then want to sort these in the order of template's children\n // TODO: Extract sortFieldChildren(field)\n\n var templateChildKeys = this.getFieldChildrenKeys(template); // We flip the child keys into an object where the values are the\n // indices of the child keys array\n\n templateChildKeys = lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default()(templateChildKeys, _toConsumableArray(templateChildKeys.keys())); // This makes it easier to sort\n\n field.children = lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default()(field.children, function (child) {\n return templateChildKeys[child.pathFragment];\n });\n }\n\n this.updateDictionary(newFields);\n return field;\n }\n /**\n * Remove data from the given path.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path to remove.\n */\n\n }, {\n key: \"removeValue\",\n value: function removeValue(data, path) {\n this.removePath(path, data);\n }\n /**\n * Remove data at the given path and update parent fields.\n *\n * TODO: The naming and existence of this method doesn't quite make sense.\n * You'd expect it to remove the field(s) too.\n * Refactor!\n *\n * @protected\n * @param {string} path - The path to remove.\n * @param {Object} data - The data to remove the path from.\n */\n\n }, {\n key: \"removePath\",\n value: function removePath(path, data) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Remove the state data\n\n\n this.removeData(field, data); // Update the parent field\n\n this.updateField(this.getFieldParent(field), data);\n }\n /**\n * Remove the given fields.\n *\n * @protected\n * @param {Field[]} fields - The fields to remove.\n */\n\n }, {\n key: \"removeFields\",\n value: function removeFields(fields) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.removeField(fields[i]);\n }\n }\n /**\n * Remove a field.\n *\n * Clears dictionary and parent references to the field.\n *\n * Doesn't remove data or update parent field values.\n *\n * @protected\n * @param {Field} field - The field to remove.\n */\n\n }, {\n key: \"removeField\",\n value: function removeField(field) {\n if (!field) {\n return;\n }\n\n this.clearValueCache(field.path); // Remove the field's children\n\n this.removeFields(field.children); // Remove the field from the dictionary and dependency list\n\n delete this.dictionary[field.path];\n delete this.fieldDependencies[field.path]; // Remove the field from its parent\n\n var parent = this.getFieldParent(field);\n lodash_pull__WEBPACK_IMPORTED_MODULE_9___default()(parent.children, field);\n }\n /**\n * Remove a field's path from the given data.\n *\n * @param {Field} field - The field whose path to remove from data.\n * @param {Object} data - The data to remove from.\n */\n\n }, {\n key: \"removeData\",\n value: function removeData(field, data) {\n if (!field) {\n return;\n }\n\n var parent = this.getField(field.parent);\n\n if (!parent) {\n return;\n }\n\n var parentPath = parent.path;\n var parentValue = this.getFieldValue(parent, data);\n var key = field.pathFragment;\n\n if (Array.isArray(parentValue)) {\n parentValue.splice(key, 1);\n } else {\n delete parentValue[key];\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, parentPath, parentValue);\n }\n /**\n * Update the dictionary with the given fields.\n *\n * @param {Field[]} fields\n * @returns {FieldDictionary}\n */\n\n }, {\n key: \"updateDictionary\",\n value: function updateDictionary(fields) {\n if (!Array.isArray(fields)) {\n return this.dictionary;\n }\n\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i] || !fields[i].path) {\n continue;\n }\n\n this.dictionary[fields[i].path] = fields[i];\n }\n\n return this.dictionary;\n }\n /**\n * Build a dictionary from the given fields.\n *\n * @param {Field[]} fields\n * @return {FieldDictionary}\n */\n\n }, {\n key: \"buildDictionary\",\n value: function buildDictionary(fields) {\n return Object(_functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__[\"default\"])(fields);\n }\n /**\n * Build a tree from the given dictionary.\n *\n * @protected\n * @param {FieldDictionary} dictionary\n * @returns {Field}\n */\n\n }, {\n key: \"buildTree\",\n value: function buildTree(dictionary) {\n return Object(_functions_buildTree__WEBPACK_IMPORTED_MODULE_22__[\"default\"])(dictionary);\n }\n /**\n * Build data from the current field state.\n *\n * @param {Field} field - The root field to traverse from.\n * @param {Object} [data={}] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildData\",\n value: function buildData(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (!field) {\n return data;\n }\n\n var child, childData; // If the field has children, build the data of its children\n\n if (field.children) {\n for (var c = 0; c < field.children.length; c++) {\n child = field.children[c];\n\n if (child.virtual || child.omit) {\n continue;\n }\n\n childData = this.buildData(field.children[c], data);\n }\n\n return data;\n } // Set data\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n return data;\n }\n /**\n * Build data for a field's template.\n *\n * @protected\n * @param {Field} field - The field to build child data for.\n * @param {Object} [data] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildTemplateData\",\n value: function buildTemplateData(field, data) {\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return null;\n }\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(this.buildData(template), template.path);\n }\n /**\n * Add new child data for the field at the given path using its template.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path of the field to add new child data to.\n * @param {string|number} [key] - Optional key to use for the new child data.\n */\n\n }, {\n key: \"addItem\",\n value: function addItem(data, path, key) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Build the new child data\n\n\n var newData = this.buildTemplateData(field); // Get the target for the data\n\n var target = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path, []); // Add the new child data to the collection\n\n if (Array.isArray(target)) {\n target.push(newData);\n } else if (key != null && _typeof(target) === 'object') {\n target[key] = newData;\n } else {\n // Bail if we're not dealing with a collection\n console.warn(\"Could not create new child data for '\".concat(path, \"';\") + \" either it wasn't an array or wasn't an object with a key provided\");\n return;\n } // Set it back\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, target); // Update the form\n\n this.updatePath(path, data);\n }\n }]);\n\n return FormProcessor;\n}();\n/**\n * A dictionary of fields.\n *\n * Fields are keyed by their path.\n *\n * @typedef {Object.} FieldDictionary\n */\n\n/**\n * A field description.\n *\n * TODO: Update this to reflect the simplest approach to describing fields.\n * Could also be called FieldOptions if passed to the constructor of a\n * Field class.\n *\n * @typedef {Object} FieldDescription\n */\n\n/**\n * A field.\n *\n * TODO: Formalise as a class?\n *\n * @typedef {Object} Field\n *\n * @property {string} path - The path of the field.\n * @property {string} [parent] - The path of the field's parent, if any. Overrides the parent that would otherwise be determined from the `path`.\n * @property {string} [pathFragment] - The leaf of the field's path. TODO: Rename to key\n * @property {string} [type] - The type of the field. Determines the type of value to read and store. Defaults to `'number'`.\n * @property {string} [input] - The input type to use for this field, if any. // TODO: Rename? Might not be an actual input... (i.e. section). `element` might be a good name.\n * @property {Object} [options] - The input options. A free-form object for different input types to interpret and utilise.\n * @property {string} [name] - The field's name. Defaults to a sentence-case translation of the field's key. TODO: Rename to label?\n * @property {string} [description] - The field's description.\n * @property {boolean} [omit=false] - Whether to prevent storing the property's value in data AND prevent updating any children. Defaults to `false`.\n * @property {boolean} [virtual=false] - Whether to prevent storing the property's value in data. Defaults to `false`.\n * @property {string|boolean} [visible=true] - Whether the property is visible. Defaults to `true`. String values are interpreted as expressions.\n * @property {string|boolean} [disabled=false] - Whether the property is disabled. Defaults to `true` if `expression` is set, otherwise defaults to `false`. String values are interpreted as expressions. TODO: Input options?\n * @property {*} [value] - The field's value.\n * @property {*} [default] - The field's default value. Defaults appropriately for the set `type`.\n * @property {boolean} [merge] - Whether to merge the field's non-scalar value with its default value.\n * @property {string} [expression] - An expression used to compute the field's value. Implies `disabled` when set.\n * @property {string} [validator] - The field's validation function. Defaults as appropriate to the `type`.\n * @property {string} [extends] - The path of a field to inherit.\n * @property {string} [extended] - The path of a field that been inherited.\n * @property {string} [mirror] - The path of a field to mirror. TODO: Implement\n * @property {Field[]} [children] - Child fields.\n * @property {string} [template] - Template field that all child fields should extend. Can be a `Field` or a `path` to a field.\n * @property {Object|array} [fixed] - A map or list of child keys that cannot be removed at runtime, if present.\n */\n\n\n\n\n//# sourceURL=webpack://%5Bname%5D/./src/services/FormProcessor.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return FormProcessor; });\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! expr-eval */ \"./node_modules/expr-eval/dist/bundle.js\");\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(expr_eval__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! lodash/clone */ \"./node_modules/lodash/clone.js\");\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lodash_clone__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! lodash/merge */ \"./node_modules/lodash/merge.js\");\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! lodash/defaultsDeep */ \"./node_modules/lodash/defaultsDeep.js\");\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! lodash/map */ \"./node_modules/lodash/map.js\");\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(lodash_map__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! lodash/reduce */ \"./node_modules/lodash/reduce.js\");\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(lodash_reduce__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! lodash/has */ \"./node_modules/lodash/has.js\");\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(lodash_has__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/get */ \"./node_modules/lodash/get.js\");\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_get__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! lodash/set */ \"./node_modules/lodash/set.js\");\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(lodash_set__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! lodash/pull */ \"./node_modules/lodash/pull.js\");\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(lodash_pull__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! lodash/defaultTo */ \"./node_modules/lodash/defaultTo.js\");\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! lodash/difference */ \"./node_modules/lodash/difference.js\");\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(lodash_difference__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! lodash/intersection */ \"./node_modules/lodash/intersection.js\");\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(lodash_intersection__WEBPACK_IMPORTED_MODULE_12__);\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lodash/isPlainObject */ \"./node_modules/lodash/isPlainObject.js\");\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__);\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! lodash/toNumber */ \"./node_modules/lodash/toNumber.js\");\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__);\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! lodash/zipObject */ \"./node_modules/lodash/zipObject.js\");\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__);\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! lodash/sortBy */ \"./node_modules/lodash/sortBy.js\");\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__);\n/* harmony import */ var _mixins_util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../mixins/util */ \"./src/mixins/util.js\");\n/* harmony import */ var _functions_sum__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../functions/sum */ \"./src/functions/sum.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! lodash/sumBy */ \"./node_modules/lodash/sumBy.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default = /*#__PURE__*/__webpack_require__.n(lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__);\n/* harmony import */ var _functions_multiply__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../functions/multiply */ \"./src/functions/multiply.js\");\n/* harmony import */ var _functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../functions/buildDictionary */ \"./src/functions/buildDictionary.js\");\n/* harmony import */ var _functions_buildTree__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../functions/buildTree */ \"./src/functions/buildTree.js\");\n/* harmony import */ var _functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../functions/traverseTree */ \"./src/functions/traverseTree.js\");\n/* harmony import */ var _functions_splitPath__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../functions/splitPath */ \"./src/functions/splitPath.js\");\n/* harmony import */ var _functions_joinPath__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../functions/joinPath */ \"./src/functions/joinPath.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! flat */ \"./node_modules/flat/index.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(flat__WEBPACK_IMPORTED_MODULE_26__);\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! deep-object-diff */ \"./node_modules/deep-object-diff/dist/index.js\");\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27___default = /*#__PURE__*/__webpack_require__.n(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); }\n\nfunction _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar UP = -1;\nvar BOTH = 0;\nvar DOWN = 1;\n/**\n * Pragma form.\n *\n * Expands field lists a dictionary and tree. Processes field expressions from\n * state data.\n *\n * TODO: Rename to Form?\n *\n * @class FormProcessor\n */\n\nvar FormProcessor =\n/*#__PURE__*/\nfunction () {\n /**\n * Create a new property processor.\n *\n * @constructor\n * @param {Field[]} [fields=[]] - Initial form fields.\n * @param {Object.} [functions={}] - Functions to make available for field expressions.\n * @param {Object.} [inputOptions={}] - Default input options keyed by input type.\n */\n function FormProcessor() {\n var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var functions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var inputOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n _classCallCheck(this, FormProcessor);\n\n /**\n * Typecasting functions for each field type.\n *\n * TODO: Strong casting functions\n *\n * @type {Object.}\n */\n this.casts = {\n 'string': function string(f, v) {\n return v == null ? '' : '' + v;\n },\n 'number': function number(f, v) {\n return lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default()(_mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].clamp(v, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.min'), lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.max')));\n },\n 'boolean': function boolean(f, v) {\n return !!v;\n }\n };\n /**\n * Default property values for each field type.\n *\n * @type {Object}\n */\n\n this.defaults = {};\n this.setDefaults({\n '*': {\n type: 'number',\n visible: true\n },\n 'virtual': {\n visible: false,\n virtual: true,\n omit: true\n },\n 'string': {\n input: 'string',\n default: ''\n },\n 'number': {\n input: 'number',\n default: 0,\n options: {\n min: -100,\n max: 100,\n step: 1\n }\n },\n 'boolean': {\n input: 'boolean',\n default: false\n },\n 'selection': {\n input: 'selection',\n options: {\n options: {}\n }\n },\n 'section': {\n input: 'section'\n },\n 'group': {\n input: 'group'\n },\n 'list': {\n input: 'list'\n },\n 'list-item': {\n input: 'list-item'\n },\n 'table': {\n input: 'pragma-table'\n }\n });\n /**\n * Default input options for each input type.\n *\n * @type {Object.}\n */\n\n this.inputOptions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, inputOptions);\n /**\n * Expression functions.\n *\n * @type {Object.}\n */\n\n this.functions = {};\n /**\n * Expression parser.\n *\n * @type {Parser}\n */\n\n this.parser = new expr_eval__WEBPACK_IMPORTED_MODULE_0___default.a.Parser({\n operators: {\n in: true\n }\n }); // Set the functions\n\n this.addFunctions(lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({\n concat: function concat() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return args.join('');\n },\n keys: Object.keys,\n multiply: _functions_multiply__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n sum: _functions_sum__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n sumBy: lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default.a,\n map: lodash_map__WEBPACK_IMPORTED_MODULE_4___default.a,\n reduce: lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default.a\n }, functions));\n /**\n * The set of form fields.\n *\n * @type {Field[]}\n */\n\n this.fields = [];\n /**\n * Fields keyed by path.\n *\n * @type {FieldDictionary}\n */\n\n this.dictionary = {};\n /**\n * The root node of the field tree.\n *\n * @type {Field}\n */\n\n this.tree = {}; // Set the form fields\n\n this.setFields(fields);\n /**\n * Value cache for each field.\n *\n * @type {Object.}\n */\n\n this.valueCache = {};\n /**\n * Field expression cache keyed by path.\n *\n * @type {Object.}\n */\n\n this.expressionCache = {};\n /**\n * Field update dependencies keyed by path.\n *\n * @type {Object.}\n */\n\n this.fieldDependencies = {};\n /**\n * Map of updated fields.\n *\n * Used to prevent updating fields more than once.\n *\n * @type {Object.}\n */\n\n this.updatedFields = {};\n }\n /**\n * Check whether a field exists at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to check.\n * @return {boolean}\n */\n\n\n _createClass(FormProcessor, [{\n key: \"hasField\",\n value: function hasField(path) {\n return lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(this.dictionary, path);\n }\n /**\n * Get the field at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to get.\n * @return {Field}\n */\n\n }, {\n key: \"getField\",\n value: function getField(path) {\n return this.dictionary[path];\n }\n /**\n * Get the parent field of the field at the given path.\n *\n * @protected\n * @param {Field} field\n * @return {Field|null}\n */\n\n }, {\n key: \"getFieldParent\",\n value: function getFieldParent(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.parent);\n }\n /**\n * Get the ancestors of a field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]}\n */\n\n }, {\n key: \"getFieldAncestors\",\n value: function getFieldAncestors(field) {\n var ancestors = [];\n\n while (field.hasOwnProperty('parent') && this.hasField(field.parent)) {\n field = this.getFieldParent(field);\n ancestors.push(field);\n }\n\n return ancestors;\n }\n /**\n * Get the current value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the value of.\n * @param {*} [data={}] - Optional data to read current values from.\n * @param {*} [value] - Optional current value.\n * @return {*} The current value of the field.\n */\n\n }, {\n key: \"getFieldValue\",\n value: function getFieldValue(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n\n if (!field) {\n return value;\n }\n\n value = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path)); // Merge default values if specified\n\n if (field.merge) {\n if (lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default()(field.default)) {\n return lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, field.default, field.value, value);\n } // TODO: Handle arrays\n\n } // Otherwise use the first defined value\n\n\n return lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n }\n /**\n * Get the default value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the default value of.\n * @return {*} The default value of the field.\n */\n\n }, {\n key: \"getFieldDefaultValue\",\n value: function getFieldDefaultValue(field) {\n if (!field) {\n return null;\n }\n\n return field.default;\n }\n /**\n * Get the template field that a field should a extend.\n *\n * @param {Field} field - The field to get the template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldTemplate\",\n value: function getFieldTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.extends);\n }\n /**\n * Get the keys of the field's children.\n *\n * @protected\n * @param {Field} field - The field to get the child keys of.\n * @return {array} The keys of the field's children.\n */\n\n }, {\n key: \"getFieldChildrenKeys\",\n value: function getFieldChildrenKeys(field) {\n var i,\n keys = [],\n children = field.children || [];\n\n for (i = 0; i < children.length; i++) {\n keys.push(children[i].pathFragment);\n }\n\n return keys;\n }\n /**\n * Get the template that a field's children should extend.\n *\n * @protected\n * @param {Field} field - The field to get the child template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldChildrenTemplate\",\n value: function getFieldChildrenTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.template);\n }\n /**\n * Get the fields dependent upon the given field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]} The dependent fields\n */\n\n }, {\n key: \"getFieldDependencies\",\n value: function getFieldDependencies(field) {\n var dependencies = this.fieldDependencies[field.path]; // Skip if the field has no dependencies\n\n if (!dependencies || !dependencies.length) {\n return [];\n }\n\n var fields = [];\n\n for (var i = 0; i < dependencies.length; i++) {\n var dependency = this.getField(dependencies[i]);\n\n if (!dependency) {\n continue;\n }\n\n fields.push(dependency);\n }\n\n return fields;\n }\n /**\n * Get the current value of a field.\n *\n * Falls back to default values as appropriate.\n *\n * @public\n * @param {string} path - The path to the field.\n * @return {*} The value of the field\n */\n\n }, {\n key: \"getValue\",\n value: function getValue(path) {\n return this.getFieldValue(this.getField(path));\n }\n /**\n * Cast a value based on the property it belongs to.\n *\n * @public\n * @param {Field} field\n * @param {*} value\n */\n\n }, {\n key: \"castValue\",\n value: function castValue(field, value) {\n if (!field) return value;\n if (!this.casts[field.type]) return value; // if (Array.isArray(value))\n // \treturn value.map(this.casts[field.type]);\n\n value = this.casts[field.type](field, value);\n return value;\n }\n /**\n * Derive a field's value from some data.\n *\n * @protected\n * @param {string} path - The path of the field to derive a value for.\n * @param {Object} data - The data to derive values from.\n * @return {*} The derived value.\n */\n\n }, {\n key: \"deriveValue\",\n value: function deriveValue(path, data) {\n // Return from the value cache if a value is set\n if (this.valueCache.hasOwnProperty(path)) {\n return this.valueCache[path];\n }\n\n var field = this.getField(path);\n var value = this.getFieldValue(field, data); // Return the raw value if there's no such field\n\n if (!field) {\n return value;\n } // Cast the value\n\n\n value = this.castValue(field, value); // Evaluate the field's expression\n\n value = this.evaluateFieldExpression(field, data, value); // Fall back to defaults\n //value = defaultTo(value, defaultTo(field.default, null));\n\n value = this.getFieldValue(field, data, value); // Update the value cache\n\n this.valueCache[path] = value;\n return value;\n }\n /**\n * Build a field's expression.\n *\n * @param {Field} field - The field to build an expression for.\n * @return {Expression} The built expression.\n */\n\n }, {\n key: \"buildFieldExpression\",\n value: function buildFieldExpression(field) {\n if (field.expression == null || typeof field.expression !== 'string') {\n return null;\n } // Use the cached expression if one is available\n\n\n if (this.expressionCache[field.path]) {\n return this.expressionCache[field.path];\n } // Build the initial expression\n\n\n var expression;\n\n try {\n expression = this.parser.parse(field.expression);\n } catch (error) {\n console.error(\"Error parsing expression for field '\".concat(field.path, \"': \").concat(error.message));\n return null;\n } // Substitute contextual variables\n\n\n var substitutions = {\n $parent: field.parent\n };\n\n for (var s in substitutions) {\n try {\n expression = expression.substitute(s, substitutions[s]);\n } catch (error) {\n console.error(\"Error substituting expression variable '\".concat(s, \"' for field '\").concat(field.path, \": \").concat(error.message));\n return null;\n }\n }\n\n this.expressionCache[field.path] = expression;\n return expression;\n }\n /**\n * Evaluate a field's value from its expression.\n *\n * Causes the evaluation of any field dependencies as a result.\n *\n * TODO: Evaluate (and cache) expressions for other field properties! :D\n *\n * @param {Field} field - The field to compute the value of.\n * @param {Object} data - The data to derive values from.\n * @param {*} [value] - The current value of the field.\n * @return {*} The computed value of the field's expression.\n */\n\n }, {\n key: \"evaluateFieldExpression\",\n value: function evaluateFieldExpression(field, data, value) {\n var _this = this;\n\n value = this.getFieldValue(field, data, value); // Parse the expression\n\n var expression = this.buildFieldExpression(field);\n\n if (!expression) {\n return value;\n } // TODO: Extract deriving variables and building contextual functions\n // let variables = buildExpressionContext(field, data, expression, value)?\n // Contextual functions should still update the same \"variables\" reference\n // Derive values for the variables in the expression\n\n\n var variables = expression.variables({\n withMembers: true\n });\n var values = {\n $this: field,\n $value: value\n };\n\n for (var v = 0; v < variables.length; v++) {\n var variable = variables[v];\n if (lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(values, variable)) continue;\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(values, variable, this.deriveValue(variable, data));\n } // Build contextual functions\n\n\n values = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(values, {\n field: function field(path) {\n variables.push(path);\n return _this.getField(path);\n },\n value: function value(path) {\n // Add the path to the list of expression variables\n variables.push(path); // Derive the value for the expression\n\n return _this.deriveValue(path, data);\n }\n }); // Evaluate the expression\n\n try {\n value = expression.evaluate(values);\n } catch (error) {\n console.log('evaluateFieldExpression', field, data, value);\n console.error(\"Error evaluating expression for field '\".concat(field.path, \"': \").concat(error.message));\n } //console.log('evaluateFieldExpression', field.path, expression.toString(), variables, values, value);\n //console.log('evaluateFieldExpression expression', expression);\n // Update the map of field update dependencies\n // TODO: Exclude contextual variables\n // TODO: Move this to an earlier processing step that evaluates the\n // expression with spy functions\n\n\n for (var _v = 0; _v < variables.length; _v++) {\n var _variable = variables[_v];\n this.fieldDependencies[_variable] = this.fieldDependencies[_variable] || [];\n\n if (this.fieldDependencies[_variable].indexOf(field.path) < 0) {\n this.fieldDependencies[_variable].push(field.path);\n }\n }\n\n return value;\n }\n /**\n * Prepare fields from a set of field descriptions.\n *\n * Ascertain's the parent path and path fragment (key) of each field.\n *\n * TODO: Field class, FieldDescription typedef.\n *\n * @protected\n * @param {Field[]} fields - The field description.\n * @returns {Field[]} The given fields prepared with pathFragment and parent properties.\n */\n\n }, {\n key: \"prepareFields\",\n value: function prepareFields(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field, pathFragment, parentPath;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Ascertain a parent path and path fragment\n\n var _splitPath = Object(_functions_splitPath__WEBPACK_IMPORTED_MODULE_24__[\"default\"])(field.path);\n\n var _splitPath2 = _slicedToArray(_splitPath, 2);\n\n parentPath = _splitPath2[0];\n pathFragment = _splitPath2[1];\n field.pathFragment = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.pathFragment, pathFragment);\n field.parent = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.parent, parentPath);\n }\n\n return fields;\n }\n /**\n * Set the form's fields.\n *\n * TODO: Creates, updates and removes fields accordingly.\n *\n * @public\n * @param {Field[]} fields\n */\n\n }, {\n key: \"setFields\",\n value: function setFields(fields) {\n // Prepare the fields\n this.fields = this.prepareFields(fields); // TODO: Build dictionary from given fields, compare with current fields\n //this.dictionary = this.buildDictionary(this.fields);\n\n this.dictionary = this.updateDictionary(this.fields); // Compose the fields into a tree\n\n this.tree = this.buildTree(this.dictionary); // Clear all caches\n\n this.valueCache = {}; //this.expressionCache = {};\n\n this.fieldDependencies = {};\n }\n /**\n * Add a form field.\n *\n * @param {Field} field\n */\n\n }, {\n key: \"addField\",\n value: function addField(field) {\n var parent = this.getFieldParent(field);\n\n if (!parent) {\n console.warn(\"Could not set field \".concat(field.path, \" - its parent does not exist\"));\n return;\n }\n\n if (!parent.children) {\n parent.children = [];\n }\n\n if (!parent.children.includes(field)) {\n parent.children.push(field);\n }\n\n this.dictionary[field.path] = field;\n }\n /**\n * Update a property with the given value.\n *\n * @public\n * @param {Object} data - The data to update.\n * @param {string} path - The path of the field to update.\n * @param {*} value - The value to set.\n * @return {*} The updated value\n */\n\n }, {\n key: \"setValue\",\n value: function setValue(data, path, value) {\n var field = this.getField(path); // Update the value if one is given\n\n if (value !== undefined) {\n if (field) {\n field.value = value;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, value);\n } // Update the field at this path\n\n\n this.updatePath(path, data); // Get the updated value\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path);\n }\n /**\n * Clear the value cache.\n *\n * Optionally accepts a path to clear.\n *\n * @param {string} path\n */\n\n }, {\n key: \"clearValueCache\",\n value: function clearValueCache() {\n var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n\n // Just empty the entire cache if there's no path is or there's no field\n // at the given path\n if (!path || !this.getField(path)) {\n this.valueCache = {};\n return;\n }\n\n var i,\n field = this.getField(path); // Clear cached values of child fields iteratively\n\n var child,\n children = field.children,\n nextChildren = [];\n\n while (children && children.length) {\n nextChildren = [];\n\n for (i = 0; i < children.length; i++) {\n child = children[i];\n delete this.valueCache[child.path];\n\n if (child.children) {\n nextChildren = nextChildren.concat(child.children);\n }\n }\n\n children = nextChildren;\n } // Clear the cached value for this field\n\n\n delete this.valueCache[field.path]; // Clear cached values of parent fields iteratively\n\n var ancestors = this.getFieldAncestors(field);\n\n for (i = 0; i < ancestors.length; i++) {\n delete this.valueCache[ancestors[i].path];\n }\n }\n /**\n * Derive a property's name from its path.\n *\n * @protected\n * @param {Field} field\n * @return {string} The derived name\n */\n\n }, {\n key: \"deriveName\",\n value: function deriveName(field) {\n var path = field.path;\n var lastDotIndex = path.lastIndexOf('.');\n return _mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].sentenceCase(path.substring(lastDotIndex + 1));\n }\n /**\n * Set the default field properties for each type.\n *\n * @param {Object.} fieldProperties - The default field properties, keyed by field type.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(fieldProperties) {\n this.defaults = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.defaults, fieldProperties);\n }\n /**\n * Apply the form's default properties to the given fields.\n *\n * Fills in default values, derives default names.\n *\n * @protected\n * @param {Field[]} fields - The fields to apply default values to.\n * @returns {Field[]}\n */\n\n }, {\n key: \"applyDefaults\",\n value: function applyDefaults(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Derive a name\n\n if (field.name === undefined) {\n field.name = this.deriveName(field);\n } // Apply global defaults\n\n\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults['*']); // Apply type-specific defaults\n\n if (this.defaults[field.type]) {\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults[field.type]);\n } // Apply default input options\n\n\n if (this.inputOptions[field.input]) {\n field.options = field.options || {};\n field.options = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field.options, this.inputOptions[field.input]);\n } // Disable the field implicitly if it has an expression\n\n\n if (!field.hasOwnProperty('disabled')) {\n field.disabled = !!field.expression;\n }\n }\n\n return fields;\n }\n /**\n * Add to the form's functions.\n *\n * @param {Object.} functions - Functions to add, keyed by name.\n */\n\n }, {\n key: \"addFunctions\",\n value: function addFunctions(functions) {\n // Add the functions to the form\n this.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.functions, functions); // Add the functions to the expression parser\n\n this.parser.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.parser.functions, this.functions); // Clear the expression cache\n //this.expressionCache = {};\n }\n /**\n * Update the form using the given data.\n *\n * @public\n * @param {Object} [data] - The data to update with.\n */\n\n }, {\n key: \"update\",\n value: function update(data) {\n // Clear updated fields map\n this.updatedFields = {};\n console.time('buildData');\n var formData = this.buildData(this.tree);\n console.timeEnd('buildData');\n console.log(formData);\n console.time('diff');\n var diff = Object(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__[\"detailedDiff\"])(formData, data);\n console.timeEnd('diff');\n console.log(diff);\n console.time('flatten');\n var addedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.added);\n var updatedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.updated);\n var deletedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.deleted);\n console.timeEnd('flatten');\n console.log(addedPaths, updatedPaths, deletedPaths); //let path;\n // for (path in addedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // for (path in updatedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n //\n // for (path in deletedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // Update the value of every field\n\n this.updatePath('', data);\n console.time('traverseTree');\n Object(_functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__[\"default\"])(this.tree, function (field) {// Pre-order\n }, function (field) {// Post-order\n //console.log(field.path);\n });\n console.timeEnd('traverseTree');\n }\n /**\n * Update the field at the given path.\n *\n * @param {string} path - The path of the field to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updatePath\",\n value: function updatePath(path, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n this.updateField(this.getField(path), data, direction);\n }\n /**\n * Update the given fields with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field[]} fields - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateFields\",\n value: function updateFields(fields, data, direction) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.updateField(fields[i], data, direction);\n }\n }\n /**\n * Update the given field with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field} field - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateField\",\n value: function updateField(field, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n\n if (!field) {\n return;\n } //console.log('updateField()', field.path);\n //console.count('updateField()');\n\n\n this.clearValueCache(field.path); // Update the field's children\n\n if (direction >= 0) {\n // TODO: This would be a good spot for an event to fire to allow plugins\n // (like inheritance) to intercept child update behaviour\n this.updateFieldInheritance(field, data);\n\n if (!field.omit) {\n this.updateFields(field.children, data);\n }\n } // Skip if this field has already been updated\n\n\n if (this.updatedFields[field.path]) {\n return;\n } // Apply default values\n\n\n this.applyDefaults([field]); // Update the state's value\n\n this.updateDataValue(field, data); // Update the field's value (and update all fields dependent on this one)\n\n this.updateFieldValue(field, data, direction <= 0); // Mark the field as updated\n\n this.updatedFields[field.path] = true;\n }\n /**\n * Update data from the given field.\n *\n * @protected\n * @param {Field} field - The field to update with.\n * @param {Object} data - The data to update.\n * @return {Object} The updated data.\n */\n\n }, {\n key: \"updateDataValue\",\n value: function updateDataValue(field, data) {\n if (!field || field.omit || field.virtual) {\n return data;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, this.deriveValue(field.path, data));\n return data;\n }\n /**\n * Update a field using the given data.\n *\n * @protected\n * @param {Field} field - The field to update.\n * @param {Object} data - The data to update with.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldValue\",\n value: function updateFieldValue(field, data) {\n var updateParents = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!field) {\n return;\n } // Update the field value\n\n\n field.value = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path); // Update all fields dependent on this one\n\n this.updateFieldDependencies(field, data, updateParents);\n }\n /**\n * Update fields that are dependent upon the value of the given field.\n *\n * @protected\n * @param {Field} field - The field to update dependencies of.\n * @param {Object} data - The data to update from.\n * @param {boolean=false} updateAncestors - Whether to update the field's ancestors.\n */\n\n }, {\n key: \"updateFieldDependencies\",\n value: function updateFieldDependencies(field, data, updateAncestors) {\n // Recursively update parent field values\n if (updateAncestors) {\n this.updateFieldAncestorValues(field, data);\n } // Update fields listed as dependencies\n\n\n this.updateFields(this.getFieldDependencies(field), data);\n }\n /**\n * Update the ancestors of the given field.\n *\n * @protected\n * @param {Field} field - The field to update parents of.\n * @param {Object} data - The data to update with.\n */\n\n }, {\n key: \"updateFieldAncestorValues\",\n value: function updateFieldAncestorValues(field, data) {\n var i,\n parents = this.getFieldAncestors(field);\n\n for (i = 0; i < parents.length; i++) {\n this.updateFieldValue(parents[i], data);\n }\n }\n /**\n * Update a field's inheritance.\n *\n * Ensures that inherited child fields exist.\n *\n * @param {Field} field - The field to update the inheritance of.\n * @param {Object} data - The date to update with.\n */\n\n }, {\n key: \"updateFieldInheritance\",\n value: function updateFieldInheritance(field, data) {\n // Update child template fields\n this.updateTemplateFields(field, data); // Inherit the field's template\n\n this.inheritTemplate(field, data);\n }\n /**\n * Get the keys of child fields that don't exist in the given data.\n *\n * Retrieves the new keys, existing keys and old keys of a field compared\n * to its data.\n *\n * @protected\n * @param {Field} field - The field with a template.\n * @param {Object} data - The data to diff against.\n * @return array [newPaths[], existingPaths[], oldPaths[]]\n */\n\n }, {\n key: \"diffFieldDataKeys\",\n value: function diffFieldDataKeys(field, data) {\n // Grab the data keys and child field keys\n var childData = this.getFieldValue(field, data);\n var childDataKeys = childData ? Object.keys(childData) : [];\n var childFieldKeys = this.getFieldChildrenKeys(field); // Keys in data that aren't in fields\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childDataKeys, childFieldKeys); // Keys in data and fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(childDataKeys, childFieldKeys); // Keys in fields that aren't in data\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childFieldKeys, childDataKeys); // console.log('diffTemplateFieldKeys()', field.path, 'newKeys', newKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'existingKeys', existingKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'oldKeys', oldKeys);\n\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Unravel all templates into fields for the given field and data.\n *\n * TODO: Try to merge this into inheritTemplate(), or extract a method\n * that can handle both cases, like updateFieldChildrenInheritance().\n *\n * @protected\n * @param {Field} field - The field to update template fields for.\n * @param {Object} [data] - The data used to unravel field templates.\n */\n\n }, {\n key: \"updateTemplateFields\",\n value: function updateTemplateFields(field, data) {\n if (!field) {\n return;\n }\n\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return;\n }\n\n var dictionary = this.dictionary,\n i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Find child fields that need to be added, updated or removed\n\n var _this$diffFieldDataKe = this.diffFieldDataKeys(field, data),\n _this$diffFieldDataKe2 = _slicedToArray(_this$diffFieldDataKe, 3),\n newKeys = _this$diffFieldDataKe2[0],\n existingKeys = _this$diffFieldDataKe2[1],\n oldKeys = _this$diffFieldDataKe2[2];\n\n var existingFieldKeys = this.getFieldChildrenKeys(field);\n var fixedKeys = field.fixed || []; // TODO: Extract to... diffFixedFieldDataKeys()...?\n // Remove fixed keys from the keys that need removing\n\n oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(oldKeys, fixedKeys); // Add the existent fixed keys to the keys that need updating\n\n existingKeys = existingKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(fixedKeys, existingFieldKeys), existingKeys, newKeys)); // Add the non-existent fixed keys to the keys that need creating\n\n newKeys = newKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(fixedKeys, existingFieldKeys, newKeys)); //console.log(field.path, newKeys, existingKeys, oldKeys, fixedKeys, existingFieldKeys);\n // Remove old fields\n\n for (i = 0; i < oldKeys.length; i++) {\n key = oldKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n this.removeField(this.getField(path));\n } // Update existing fields\n\n\n for (i = 0; i < existingKeys.length; i++) {\n key = existingKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() existingField', field.path, path);\n // Ensure the existing field has the correct template\n\n existingField = this.getField(path);\n existingField.extends = template.path;\n } // Build new fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field);\n\n for (i = 0; i < newKeys.length; i++) {\n key = newKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() newField', path, value[key]);\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: template.path\n };\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields);\n }\n\n this.updateDictionary(newFields);\n }\n /**\n * Diff the keys of the first field's children with those of the second\n * field's children.\n *\n * Finds keys of the first that aren't of the second, keys that are of both,\n * and keys of the second that aren't of the first.\n *\n * @protected\n * @param {Field} firstField\n * @param {Field} secondField\n * @returns {array} [newKeys, existingKeys, oldKeys]\n */\n\n }, {\n key: \"diffFieldChildrenKeys\",\n value: function diffFieldChildrenKeys(firstField, secondField) {\n var firstFieldKeys = this.getFieldChildrenKeys(firstField);\n var secondFieldKeys = this.getFieldChildrenKeys(secondField); // Child keys of the first field that aren't of the second field\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(firstFieldKeys, secondFieldKeys); // Keys in the children of both fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(firstFieldKeys, secondFieldKeys); // Child keys of the second field that aren't of the first\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(secondFieldKeys, firstFieldKeys);\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Apply a field's inheritance.\n *\n * Ensures that a field inherits from its base field.\n *\n * @param {Field} field - The inheriting field.\n * @param {Object} data - The data to update with.\n * @return {Field}\n */\n\n }, {\n key: \"inheritTemplate\",\n value: function inheritTemplate(field, data) {\n if (!field) {\n return field;\n }\n\n var template = this.getFieldTemplate(field); // Skip fields without a template\n\n if (!template) {\n return field;\n } // Inherit the template\n\n\n if (field.extended !== template.path) {\n var templateClone = lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(template); // We don't want to inherit children, nor do we support more than a\n // single layer of inheritance\n\n delete templateClone.children;\n delete templateClone.template; // if (field.path === 'skills.list.test.ability') {\n // \tconsole.log('skills.list.test.ability', clone(field), templateClone);\n // }\n // Merge sandwich to retain original values\n\n field = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(field, templateClone, lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(field));\n\n if (field.name == null) {\n field.name = this.deriveName(field);\n }\n\n field.extended = template.path;\n this.prepareFields([field]);\n } //console.log('inheritTemplate()', field.path, template.path);\n // Update child field inheritance\n\n\n var i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Diff field child keys and template child keys\n\n var _this$diffFieldChildr = this.diffFieldChildrenKeys(template, field),\n _this$diffFieldChildr2 = _slicedToArray(_this$diffFieldChildr, 2),\n newKeys = _this$diffFieldChildr2[0],\n existingKeys = _this$diffFieldChildr2[1]; // Update existing template fields\n\n\n for (var _i2 = 0; _i2 < existingKeys.length; _i2++) {\n key = existingKeys[_i2];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n existingField = this.getField(path); //console.log('inheritTemplate() existingField', path, existingField);\n\n existingField.extends = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key);\n } // Build new template fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field); //console.log('inheritTemplate()', field.path, value);\n\n for (var _i3 = 0; _i3 < newKeys.length; _i3++) {\n key = newKeys[_i3];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('inherit newKey', path, value[key]);\n // Build the new field\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key)\n }; // Propagate values\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n // TODO: Extract addFieldChildren(field, children)\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields); // We then want to sort these in the order of template's children\n // TODO: Extract sortFieldChildren(field)\n\n var templateChildKeys = this.getFieldChildrenKeys(template); // We flip the child keys into an object where the values are the\n // indices of the child keys array\n\n templateChildKeys = lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default()(templateChildKeys, _toConsumableArray(templateChildKeys.keys())); // This makes it easier to sort\n\n field.children = lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default()(field.children, function (child) {\n return templateChildKeys[child.pathFragment];\n });\n }\n\n this.updateDictionary(newFields);\n return field;\n }\n /**\n * Remove data from the given path.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path to remove.\n */\n\n }, {\n key: \"removeValue\",\n value: function removeValue(data, path) {\n this.removePath(path, data);\n }\n /**\n * Remove data at the given path and update parent fields.\n *\n * TODO: The naming and existence of this method doesn't quite make sense.\n * You'd expect it to remove the field(s) too.\n * Refactor!\n *\n * @protected\n * @param {string} path - The path to remove.\n * @param {Object} data - The data to remove the path from.\n */\n\n }, {\n key: \"removePath\",\n value: function removePath(path, data) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Remove the state data\n\n\n this.removeData(field, data); // Update the parent field\n\n this.updateField(this.getFieldParent(field), data);\n }\n /**\n * Remove the given fields.\n *\n * @protected\n * @param {Field[]} fields - The fields to remove.\n */\n\n }, {\n key: \"removeFields\",\n value: function removeFields(fields) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.removeField(fields[i]);\n }\n }\n /**\n * Remove a field.\n *\n * Clears dictionary and parent references to the field.\n *\n * Doesn't remove data or update parent field values.\n *\n * @protected\n * @param {Field} field - The field to remove.\n */\n\n }, {\n key: \"removeField\",\n value: function removeField(field) {\n if (!field) {\n return;\n }\n\n this.clearValueCache(field.path); // Remove the field's children\n\n this.removeFields(field.children); // Remove the field from the dictionary and dependency list\n\n delete this.dictionary[field.path];\n delete this.fieldDependencies[field.path]; // Remove the field from its parent\n\n var parent = this.getFieldParent(field);\n lodash_pull__WEBPACK_IMPORTED_MODULE_9___default()(parent.children, field);\n }\n /**\n * Remove a field's path from the given data.\n *\n * @param {Field} field - The field whose path to remove from data.\n * @param {Object} data - The data to remove from.\n */\n\n }, {\n key: \"removeData\",\n value: function removeData(field, data) {\n if (!field) {\n return;\n }\n\n var parent = this.getField(field.parent);\n\n if (!parent) {\n return;\n }\n\n var parentPath = parent.path;\n var parentValue = this.getFieldValue(parent, data);\n var key = field.pathFragment;\n\n if (Array.isArray(parentValue)) {\n parentValue.splice(key, 1);\n } else {\n delete parentValue[key];\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, parentPath, parentValue);\n }\n /**\n * Update the dictionary with the given fields.\n *\n * @param {Field[]} fields\n * @returns {FieldDictionary}\n */\n\n }, {\n key: \"updateDictionary\",\n value: function updateDictionary(fields) {\n if (!Array.isArray(fields)) {\n return this.dictionary;\n }\n\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i] || !fields[i].path) {\n continue;\n }\n\n this.dictionary[fields[i].path] = fields[i];\n }\n\n return this.dictionary;\n }\n /**\n * Build a dictionary from the given fields.\n *\n * @param {Field[]} fields\n * @return {FieldDictionary}\n */\n\n }, {\n key: \"buildDictionary\",\n value: function buildDictionary(fields) {\n return Object(_functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__[\"default\"])(fields);\n }\n /**\n * Build a tree from the given dictionary.\n *\n * @protected\n * @param {FieldDictionary} dictionary\n * @returns {Field}\n */\n\n }, {\n key: \"buildTree\",\n value: function buildTree(dictionary) {\n return Object(_functions_buildTree__WEBPACK_IMPORTED_MODULE_22__[\"default\"])(dictionary);\n }\n /**\n * Build data from the current field state.\n *\n * @param {Field} field - The root field to traverse from.\n * @param {Object} [data={}] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildData\",\n value: function buildData(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (!field) {\n return data;\n }\n\n var child, childData; // If the field has children, build the data of its children\n\n if (field.children) {\n for (var c = 0; c < field.children.length; c++) {\n child = field.children[c];\n\n if (child.virtual || child.omit) {\n continue;\n }\n\n childData = this.buildData(field.children[c], data);\n }\n\n return data;\n } // Set data\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n return data;\n }\n /**\n * Build data for a field's template.\n *\n * @protected\n * @param {Field} field - The field to build child data for.\n * @param {Object} [data] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildTemplateData\",\n value: function buildTemplateData(field, data) {\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return null;\n }\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(this.buildData(template), template.path);\n }\n /**\n * Add new child data for the field at the given path using its template.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path of the field to add new child data to.\n * @param {string|number} [key] - Optional key to use for the new child data.\n */\n\n }, {\n key: \"addItem\",\n value: function addItem(data, path, key) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Build the new child data\n\n\n var newData = this.buildTemplateData(field); // Get the target for the data\n\n var target = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path, []); // Add the new child data to the collection\n\n if (Array.isArray(target)) {\n target.push(newData);\n } else if (key != null && _typeof(target) === 'object') {\n target[key] = newData;\n } else {\n // Bail if we're not dealing with a collection\n console.warn(\"Could not create new child data for '\".concat(path, \"';\") + \" either it wasn't an array or wasn't an object with a key provided\");\n return;\n } // Set it back\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, target); // Update the form\n\n this.updatePath(path, data);\n }\n }]);\n\n return FormProcessor;\n}();\n/**\n * A dictionary of fields.\n *\n * Fields are keyed by their path.\n *\n * @typedef {Object.} FieldDictionary\n */\n\n/**\n * A field description.\n *\n * TODO: Update this to reflect the simplest approach to describing fields.\n * Could also be called FieldOptions if passed to the constructor of a\n * Field class.\n *\n * @typedef {Object} FieldDescription\n */\n\n/**\n * A field.\n *\n * TODO: Formalise as a class?\n *\n * @typedef {Object} Field\n *\n * @property {string} path - The path of the field.\n * @property {string} [parent] - The path of the field's parent, if any. Overrides the parent that would otherwise be determined from the `path`.\n * @property {string} [pathFragment] - The leaf of the field's path. TODO: Rename to key\n * @property {string} [type] - The type of the field. Determines the type of value to read and store. Defaults to `'number'`.\n * @property {string} [input] - The input type to use for this field, if any. // TODO: Rename? Might not be an actual input... (i.e. section). `element` might be a good name.\n * @property {Object} [options] - The input options. A free-form object for different input types to interpret and utilise.\n * @property {string} [name] - The field's name. Defaults to a sentence-case translation of the field's key. TODO: Rename to label?\n * @property {string} [description] - The field's description.\n * @property {boolean} [omit=false] - Whether to prevent storing the property's value in data AND prevent updating any children. Defaults to `false`.\n * @property {boolean} [virtual=false] - Whether to prevent storing the property's value in data. Defaults to `false`.\n * @property {string|boolean} [visible=true] - Whether the property is visible. Defaults to `true`. String values are interpreted as expressions.\n * @property {string|boolean} [disabled=false] - Whether the property is disabled. Defaults to `true` if `expression` is set, otherwise defaults to `false`. String values are interpreted as expressions. TODO: Input options?\n * @property {*} [value] - The field's value.\n * @property {*} [default] - The field's default value. Defaults appropriately for the set `type`.\n * @property {boolean} [merge] - Whether to merge the field's non-scalar value with its default value.\n * @property {string} [expression] - An expression used to compute the field's value. Implies `disabled` when set.\n * @property {string} [validator] - The field's validation function. Defaults as appropriate to the `type`.\n * @property {string} [extends] - The path of a field to inherit.\n * @property {string} [extended] - The path of a field that been inherited.\n * @property {string} [mirror] - The path of a field to mirror. TODO: Implement\n * @property {Field[]} [children] - Child fields.\n * @property {string} [template] - Template field that all child fields should extend. Can be a `Field` or a `path` to a field.\n * @property {Object|array} [fixed] - A map or list of child keys that cannot be removed at runtime, if present.\n */\n\n\n\n\n//# sourceURL=webpack://%5Bname%5D/./src/services/FormProcessor.js?"); /***/ }), diff --git a/build/old.js b/build/old.js index abf2d98..8b20b61 100644 --- a/build/old.js +++ b/build/old.js @@ -3170,7 +3170,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return FormProcessor; });\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! expr-eval */ \"./node_modules/expr-eval/dist/bundle.js\");\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(expr_eval__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! lodash/clone */ \"./node_modules/lodash/clone.js\");\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lodash_clone__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! lodash/merge */ \"./node_modules/lodash/merge.js\");\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! lodash/defaultsDeep */ \"./node_modules/lodash/defaultsDeep.js\");\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! lodash/map */ \"./node_modules/lodash/map.js\");\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(lodash_map__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! lodash/reduce */ \"./node_modules/lodash/reduce.js\");\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(lodash_reduce__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! lodash/has */ \"./node_modules/lodash/has.js\");\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(lodash_has__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/get */ \"./node_modules/lodash/get.js\");\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_get__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! lodash/set */ \"./node_modules/lodash/set.js\");\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(lodash_set__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! lodash/pull */ \"./node_modules/lodash/pull.js\");\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(lodash_pull__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! lodash/defaultTo */ \"./node_modules/lodash/defaultTo.js\");\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! lodash/difference */ \"./node_modules/lodash/difference.js\");\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(lodash_difference__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! lodash/intersection */ \"./node_modules/lodash/intersection.js\");\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(lodash_intersection__WEBPACK_IMPORTED_MODULE_12__);\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lodash/isPlainObject */ \"./node_modules/lodash/isPlainObject.js\");\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__);\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! lodash/toNumber */ \"./node_modules/lodash/toNumber.js\");\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__);\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! lodash/zipObject */ \"./node_modules/lodash/zipObject.js\");\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__);\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! lodash/sortBy */ \"./node_modules/lodash/sortBy.js\");\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__);\n/* harmony import */ var _mixins_util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../mixins/util */ \"./src/mixins/util.js\");\n/* harmony import */ var _functions_sum__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../functions/sum */ \"./src/functions/sum.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! lodash/sumBy */ \"./node_modules/lodash/sumBy.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default = /*#__PURE__*/__webpack_require__.n(lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__);\n/* harmony import */ var _functions_multiply__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../functions/multiply */ \"./src/functions/multiply.js\");\n/* harmony import */ var _functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../functions/buildDictionary */ \"./src/functions/buildDictionary.js\");\n/* harmony import */ var _functions_buildTree__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../functions/buildTree */ \"./src/functions/buildTree.js\");\n/* harmony import */ var _functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../functions/traverseTree */ \"./src/functions/traverseTree.js\");\n/* harmony import */ var _functions_splitPath__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../functions/splitPath */ \"./src/functions/splitPath.js\");\n/* harmony import */ var _functions_joinPath__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../functions/joinPath */ \"./src/functions/joinPath.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! flat */ \"./node_modules/flat/index.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(flat__WEBPACK_IMPORTED_MODULE_26__);\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! deep-object-diff */ \"./node_modules/deep-object-diff/dist/index.js\");\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27___default = /*#__PURE__*/__webpack_require__.n(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); }\n\nfunction _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar UP = -1;\nvar BOTH = 0;\nvar DOWN = 1;\n/**\n * Pragma form.\n *\n * Expands field lists a dictionary and tree. Processes field expressions from\n * state data.\n *\n * TODO: Rename to Form?\n *\n * @class FormProcessor\n */\n\nvar FormProcessor =\n/*#__PURE__*/\nfunction () {\n /**\n * Create a new property processor.\n *\n * @constructor\n * @param {Field[]} [fields=[]] - Initial form fields.\n * @param {Object.} [functions={}] - Functions to make available for field expressions.\n * @param {Object.} [inputOptions={}] - Default input options keyed by input type.\n */\n function FormProcessor() {\n var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var functions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var inputOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n _classCallCheck(this, FormProcessor);\n\n /**\n * Typecasting functions for each field type.\n *\n * TODO: Strong casting functions\n *\n * @type {Object.}\n */\n this.casts = {\n 'string': function string(f, v) {\n return v == null ? '' : '' + v;\n },\n 'number': function number(f, v) {\n return lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default()(_mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].clamp(v, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.min'), lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.max')));\n },\n 'boolean': function boolean(f, v) {\n return !!v;\n }\n };\n /**\n * Default property values for each field type.\n *\n * @type {Object}\n */\n\n this.defaults = {};\n this.setDefaults({\n '*': {\n type: 'number',\n visible: true\n },\n 'virtual': {\n visible: false,\n virtual: true,\n omit: true\n },\n 'string': {\n input: 'string',\n default: ''\n },\n 'number': {\n input: 'number',\n default: 0,\n options: {\n min: -100,\n max: 100,\n step: 1\n }\n },\n 'boolean': {\n input: 'boolean',\n default: false\n },\n 'selection': {\n input: 'selection',\n options: {\n options: {}\n }\n },\n 'section': {\n input: 'section'\n },\n 'group': {\n input: 'group'\n },\n 'list': {\n input: 'list'\n },\n 'list-item': {\n input: 'list-item'\n },\n 'table': {\n input: 'pragma-table'\n }\n });\n /**\n * Default input options for each input type.\n *\n * @type {Object.}\n */\n\n this.inputOptions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, inputOptions);\n /**\n * Expression functions.\n *\n * @type {Object.}\n */\n\n this.functions = {};\n /**\n * Expression parser.\n *\n * @type {Parser}\n */\n\n this.parser = new expr_eval__WEBPACK_IMPORTED_MODULE_0___default.a.Parser({\n operators: {\n in: true\n }\n }); // Set the functions\n\n this.addFunctions(lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({\n concat: function concat() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return args.join('');\n },\n keys: Object.keys,\n multiply: _functions_multiply__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n sum: _functions_sum__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n sumBy: lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default.a,\n map: lodash_map__WEBPACK_IMPORTED_MODULE_4___default.a,\n reduce: lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default.a\n }, functions));\n /**\n * The set of form fields.\n *\n * @type {Field[]}\n */\n\n this.fields = [];\n /**\n * Fields keyed by path.\n *\n * @type {FieldDictionary}\n */\n\n this.dictionary = {};\n /**\n * The root node of the field tree.\n *\n * @type {Field}\n */\n\n this.tree = {}; // Set the form fields\n\n this.setFields(fields);\n /**\n * Value cache for each field.\n *\n * @type {Object.}\n */\n\n this.valueCache = {};\n /**\n * Field expression cache keyed by path.\n *\n * @type {Object.}\n */\n\n this.expressionCache = {};\n /**\n * Field update dependencies keyed by path.\n *\n * @type {Object.}\n */\n\n this.fieldDependencies = {};\n }\n /**\n * Check whether a field exists at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to check.\n * @return {boolean}\n */\n\n\n _createClass(FormProcessor, [{\n key: \"hasField\",\n value: function hasField(path) {\n return lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(this.dictionary, path);\n }\n /**\n * Get the field at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to get.\n * @return {Field}\n */\n\n }, {\n key: \"getField\",\n value: function getField(path) {\n return this.dictionary[path];\n }\n /**\n * Get the parent field of the field at the given path.\n *\n * @protected\n * @param {Field} field\n * @return {Field|null}\n */\n\n }, {\n key: \"getFieldParent\",\n value: function getFieldParent(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.parent);\n }\n /**\n * Get the ancestors of a field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]}\n */\n\n }, {\n key: \"getFieldAncestors\",\n value: function getFieldAncestors(field) {\n var ancestors = [];\n\n while (field.hasOwnProperty('parent') && this.hasField(field.parent)) {\n field = this.getFieldParent(field);\n ancestors.push(field);\n }\n\n return ancestors;\n }\n /**\n * Get the current value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the value of.\n * @param {*} [data={}] - Optional data to read current values from.\n * @param {*} [value] - Optional current value.\n * @return {*} The current value of the field.\n */\n\n }, {\n key: \"getFieldValue\",\n value: function getFieldValue(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n\n if (!field) {\n return value;\n }\n\n value = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path)); // Merge default values if specified\n\n if (field.merge) {\n if (lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default()(field.default)) {\n return lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, field.default, field.value, value);\n } // TODO: Handle arrays\n\n } // Otherwise use the first defined value\n\n\n return lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n }\n /**\n * Get the default value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the default value of.\n * @return {*} The default value of the field.\n */\n\n }, {\n key: \"getFieldDefaultValue\",\n value: function getFieldDefaultValue(field) {\n if (!field) {\n return null;\n }\n\n return field.default;\n }\n /**\n * Get the template field that a field should a extend.\n *\n * @param {Field} field - The field to get the template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldTemplate\",\n value: function getFieldTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.extends);\n }\n /**\n * Get the keys of the field's children.\n *\n * @protected\n * @param {Field} field - The field to get the child keys of.\n * @return {array} The keys of the field's children.\n */\n\n }, {\n key: \"getFieldChildrenKeys\",\n value: function getFieldChildrenKeys(field) {\n var i,\n keys = [],\n children = field.children || [];\n\n for (i = 0; i < children.length; i++) {\n keys.push(children[i].pathFragment);\n }\n\n return keys;\n }\n /**\n * Get the template that a field's children should extend.\n *\n * @protected\n * @param {Field} field - The field to get the child template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldChildrenTemplate\",\n value: function getFieldChildrenTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.template);\n }\n /**\n * Get the fields dependent upon the given field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]} The dependent fields\n */\n\n }, {\n key: \"getFieldDependencies\",\n value: function getFieldDependencies(field) {\n var dependencies = this.fieldDependencies[field.path]; // Skip if the field has no dependencies\n\n if (!dependencies || !dependencies.length) {\n return [];\n }\n\n var fields = [];\n\n for (var i = 0; i < dependencies.length; i++) {\n var dependency = this.getField(dependencies[i]);\n\n if (!dependency) {\n continue;\n }\n\n fields.push(dependency);\n }\n\n return fields;\n }\n /**\n * Get the current value of a field.\n *\n * Falls back to default values as appropriate.\n *\n * @public\n * @param {string} path - The path to the field.\n * @return {*} The value of the field\n */\n\n }, {\n key: \"getValue\",\n value: function getValue(path) {\n return this.getFieldValue(this.getField(path));\n }\n /**\n * Cast a value based on the property it belongs to.\n *\n * @public\n * @param {Field} field\n * @param {*} value\n */\n\n }, {\n key: \"castValue\",\n value: function castValue(field, value) {\n if (!field) return value;\n if (!this.casts[field.type]) return value; // if (Array.isArray(value))\n // \treturn value.map(this.casts[field.type]);\n\n value = this.casts[field.type](field, value);\n return value;\n }\n /**\n * Derive a field's value from some data.\n *\n * @protected\n * @param {string} path - The path of the field to derive a value for.\n * @param {Object} data - The data to derive values from.\n * @return {*} The derived value.\n */\n\n }, {\n key: \"deriveValue\",\n value: function deriveValue(path, data) {\n // Return from the value cache if a value is set\n if (this.valueCache.hasOwnProperty(path)) {\n return this.valueCache[path];\n }\n\n var field = this.getField(path);\n var value = this.getFieldValue(field, data); // Return the raw value if there's no such field\n\n if (!field) {\n return value;\n } // Cast the value\n\n\n value = this.castValue(field, value); // Evaluate the field's expression\n\n value = this.evaluateFieldExpression(field, data, value); // Fall back to defaults\n //value = defaultTo(value, defaultTo(field.default, null));\n\n value = this.getFieldValue(field, data, value); // Update the value cache\n\n this.valueCache[path] = value;\n return value;\n }\n /**\n * Build a field's expression.\n *\n * @param {Field} field - The field to build an expression for.\n * @return {Expression} The built expression.\n */\n\n }, {\n key: \"buildFieldExpression\",\n value: function buildFieldExpression(field) {\n if (field.expression == null || typeof field.expression !== 'string') {\n return null;\n } // Use the cached expression if one is available\n\n\n if (this.expressionCache[field.path]) {\n return this.expressionCache[field.path];\n } // Build the initial expression\n\n\n var expression;\n\n try {\n expression = this.parser.parse(field.expression);\n } catch (error) {\n console.error(\"Error parsing expression for field '\".concat(field.path, \"': \").concat(error.message));\n return null;\n } // Substitute contextual variables\n\n\n var substitutions = {\n $parent: field.parent\n };\n\n for (var s in substitutions) {\n try {\n expression = expression.substitute(s, substitutions[s]);\n } catch (error) {\n console.error(\"Error substituting expression variable '\".concat(s, \"' for field '\").concat(field.path, \": \").concat(error.message));\n return null;\n }\n }\n\n this.expressionCache[field.path] = expression;\n return expression;\n }\n /**\n * Evaluate a field's value from its expression.\n *\n * Causes the evaluation of any field dependencies as a result.\n *\n * TODO: Evaluate (and cache) expressions for other field properties! :D\n *\n * @param {Field} field - The field to compute the value of.\n * @param {Object} data - The data to derive values from.\n * @param {*} [value] - The current value of the field.\n * @return {*} The computed value of the field's expression.\n */\n\n }, {\n key: \"evaluateFieldExpression\",\n value: function evaluateFieldExpression(field, data, value) {\n var _this = this;\n\n value = this.getFieldValue(field, data, value); // Parse the expression\n\n var expression = this.buildFieldExpression(field);\n\n if (!expression) {\n return value;\n } // TODO: Extract deriving variables and building contextual functions\n // let variables = buildExpressionContext(field, data, expression, value)?\n // Contextual functions should still update the same \"variables\" reference\n // Derive values for the variables in the expression\n\n\n var variables = expression.variables({\n withMembers: true\n });\n var values = {\n $this: field,\n $value: value\n };\n\n for (var v = 0; v < variables.length; v++) {\n var variable = variables[v];\n if (lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(values, variable)) continue;\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(values, variable, this.deriveValue(variable, data));\n } // Build contextual functions\n\n\n values = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(values, {\n field: function field(path) {\n variables.push(path);\n return _this.getField(path);\n },\n value: function value(path) {\n // Add the path to the list of expression variables\n variables.push(path); // Derive the value for the expression\n\n return _this.deriveValue(path, data);\n }\n }); // Evaluate the expression\n\n try {\n value = expression.evaluate(values);\n } catch (error) {\n console.log('evaluateFieldExpression', field, data, value);\n console.error(\"Error evaluating expression for field '\".concat(field.path, \"': \").concat(error.message));\n } //console.log('evaluateFieldExpression', field.path, expression.toString(), variables, values, value);\n //console.log('evaluateFieldExpression expression', expression);\n // Update the map of field update dependencies\n // TODO: Exclude contextual variables\n // TODO: Move this to an earlier processing step that evaluates the\n // expression with spy functions\n\n\n for (var _v = 0; _v < variables.length; _v++) {\n var _variable = variables[_v];\n this.fieldDependencies[_variable] = this.fieldDependencies[_variable] || [];\n\n if (this.fieldDependencies[_variable].indexOf(field.path) < 0) {\n this.fieldDependencies[_variable].push(field.path);\n }\n }\n\n return value;\n }\n /**\n * Prepare fields from a set of field descriptions.\n *\n * Ascertain's the parent path and path fragment (key) of each field.\n *\n * TODO: Field class, FieldDescription typedef.\n *\n * @protected\n * @param {Field[]} fields - The field description.\n * @returns {Field[]} The given fields prepared with pathFragment and parent properties.\n */\n\n }, {\n key: \"prepareFields\",\n value: function prepareFields(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field, pathFragment, parentPath;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Ascertain a parent path and path fragment\n\n var _splitPath = Object(_functions_splitPath__WEBPACK_IMPORTED_MODULE_24__[\"default\"])(field.path);\n\n var _splitPath2 = _slicedToArray(_splitPath, 2);\n\n parentPath = _splitPath2[0];\n pathFragment = _splitPath2[1];\n field.pathFragment = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.pathFragment, pathFragment);\n field.parent = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.parent, parentPath);\n }\n\n return fields;\n }\n /**\n * Set the form's fields.\n *\n * TODO: Creates, updates and removes fields accordingly.\n *\n * @public\n * @param {Field[]} fields\n */\n\n }, {\n key: \"setFields\",\n value: function setFields(fields) {\n // Prepare the fields\n this.fields = this.prepareFields(fields); // TODO: Build dictionary from given fields, compare with current fields\n //this.dictionary = this.buildDictionary(this.fields);\n\n this.dictionary = this.updateDictionary(this.fields); // Compose the fields into a tree\n\n this.tree = this.buildTree(this.dictionary); // Clear all caches\n\n this.valueCache = {};\n this.expressionCache = {};\n this.fieldDependencies = {};\n }\n /**\n * Add a form field.\n *\n * @param {Field} field\n */\n\n }, {\n key: \"addField\",\n value: function addField(field) {\n var parent = this.getFieldParent(field);\n\n if (!parent) {\n console.warn(\"Could not set field \".concat(field.path, \" - its parent does not exist\"));\n return;\n }\n\n if (!parent.children) {\n parent.children = [];\n }\n\n if (!parent.children.includes(field)) {\n parent.children.push(field);\n }\n\n this.dictionary[field.path] = field;\n }\n /**\n * Update a property with the given value.\n *\n * @public\n * @param {Object} data - The data to update.\n * @param {string} path - The path of the field to update.\n * @param {*} value - The value to set.\n * @return {*} The updated value\n */\n\n }, {\n key: \"setValue\",\n value: function setValue(data, path, value) {\n var field = this.getField(path); // Update the value if one is given\n\n if (value !== undefined) {\n if (field) {\n field.value = value;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, value);\n } // Update the field at this path\n\n\n this.updatePath(path, data); // Get the updated value\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path);\n }\n /**\n * Clear the value cache.\n *\n * Optionally accepts a path to clear.\n *\n * @param {string} path\n */\n\n }, {\n key: \"clearValueCache\",\n value: function clearValueCache() {\n var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n\n // Just empty the entire cache if there's no path is or there's no field\n // at the given path\n if (!path || !this.getField(path)) {\n this.valueCache = {};\n return;\n }\n\n var i,\n field = this.getField(path); // Clear cached values of child fields iteratively\n\n var child,\n children = field.children,\n nextChildren = [];\n\n while (children && children.length) {\n nextChildren = [];\n\n for (i = 0; i < children.length; i++) {\n child = children[i];\n delete this.valueCache[child.path];\n\n if (child.children) {\n nextChildren = nextChildren.concat(child.children);\n }\n }\n\n children = nextChildren;\n } // Clear the cached value for this field\n\n\n delete this.valueCache[field.path]; // Clear cached values of parent fields iteratively\n\n var ancestors = this.getFieldAncestors(field);\n\n for (i = 0; i < ancestors.length; i++) {\n delete this.valueCache[ancestors[i].path];\n }\n }\n /**\n * Derive a property's name from its path.\n *\n * @protected\n * @param {Field} field\n * @return {string} The derived name\n */\n\n }, {\n key: \"deriveName\",\n value: function deriveName(field) {\n var path = field.path;\n var lastDotIndex = path.lastIndexOf('.');\n return _mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].sentenceCase(path.substring(lastDotIndex + 1));\n }\n /**\n * Set the default field properties for each type.\n *\n * @param {Object.} fieldProperties - The default field properties, keyed by field type.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(fieldProperties) {\n this.defaults = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.defaults, fieldProperties);\n }\n /**\n * Apply the form's default properties to the given fields.\n *\n * Fills in default values, derives default names.\n *\n * @protected\n * @param {Field[]} fields - The fields to apply default values to.\n * @returns {Field[]}\n */\n\n }, {\n key: \"applyDefaults\",\n value: function applyDefaults(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Derive a name\n\n if (field.name === undefined) {\n field.name = this.deriveName(field);\n } // Apply global defaults\n\n\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults['*']); // Apply type-specific defaults\n\n if (this.defaults[field.type]) {\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults[field.type]);\n } // Apply default input options\n\n\n if (this.inputOptions[field.input]) {\n field.options = field.options || {};\n field.options = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field.options, this.inputOptions[field.input]);\n } // Disable the field implicitly if it has an expression\n\n\n if (!field.hasOwnProperty('disabled')) {\n field.disabled = !!field.expression;\n }\n }\n\n return fields;\n }\n /**\n * Add to the form's functions.\n *\n * @param {Object.} functions - Functions to add, keyed by name.\n */\n\n }, {\n key: \"addFunctions\",\n value: function addFunctions(functions) {\n // Add the functions to the form\n this.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.functions, functions); // Add the functions to the expression parser\n\n this.parser.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.parser.functions, this.functions); // Clear the expression cache\n\n this.expressionCache = {};\n }\n /**\n * Update the form using the given data.\n *\n * @public\n * @param {Object} [data] - The data to update with.\n */\n\n }, {\n key: \"update\",\n value: function update(data) {\n // Update the value of every field\n this.updatePath('', data); // TODO: Diff any *paths* that changed and update those\n // i.e. implement diffFieldDataPaths()\n\n console.time('buildData');\n var formData = this.buildData(this.tree);\n console.timeEnd('buildData');\n console.log(formData);\n console.time('diff');\n var diff = Object(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__[\"detailedDiff\"])(formData, data);\n console.timeEnd('diff');\n console.log(diff);\n console.time('flatten');\n var addedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.added);\n var updatedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.updated);\n var deletedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.deleted);\n console.timeEnd('flatten');\n console.log(addedPaths, updatedPaths, deletedPaths); //let path;\n // for (path in addedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // for (path in updatedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n //\n // for (path in deletedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n\n console.time('traverseTree');\n Object(_functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__[\"default\"])(this.tree, function (field) {// Pre-order\n }, function (field) {\n // Post-order\n console.log(field.path);\n });\n console.timeEnd('traverseTree');\n }\n /**\n * Update the field at the given path.\n *\n * @param {string} path - The path of the field to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updatePath\",\n value: function updatePath(path, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n this.updateField(this.getField(path), data, direction);\n }\n /**\n * Update the given fields with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field[]} fields - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateFields\",\n value: function updateFields(fields, data, direction) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.updateField(fields[i], data, direction);\n }\n }\n /**\n * Update the given field with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field} field - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateField\",\n value: function updateField(field, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n\n if (!field) {\n return;\n } //console.log('updateField()', field.path);\n\n\n console.count('updateField()');\n this.clearValueCache(field.path); // Update the field's children\n\n if (direction >= 0) {\n // TODO: This would be a good spot for an event to fire to allow plugins\n // (like inheritance) to intercept child update behaviour\n this.updateFieldInheritance(field, data);\n\n if (!field.omit) {\n this.updateFields(field.children, data);\n }\n } // Apply default values\n\n\n this.applyDefaults([field]); // Update the state's value\n\n this.updateDataValue(field, data); // Update the field's value (and update all fields dependent on this one)\n\n this.updateFieldValue(field, data, direction <= 0);\n }\n /**\n * Update data from the given field.\n *\n * @protected\n * @param {Field} field - The field to update with.\n * @param {Object} data - The data to update.\n * @return {Object} The updated data.\n */\n\n }, {\n key: \"updateDataValue\",\n value: function updateDataValue(field, data) {\n if (!field || field.omit || field.virtual) {\n return data;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, this.deriveValue(field.path, data));\n return data;\n }\n /**\n * Update a field using the given data.\n *\n * @protected\n * @param {Field} field - The field to update.\n * @param {Object} data - The data to update with.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldValue\",\n value: function updateFieldValue(field, data) {\n var updateParents = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!field) {\n return;\n } // Update the field value\n\n\n field.value = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path); // Update all fields dependent on this one\n\n this.updateFieldDependencies(field, data, updateParents);\n }\n /**\n * Update fields that are dependent upon the value of the given field.\n *\n * @protected\n * @param {Field} field - The field to update dependencies of.\n * @param {Object} data - The data to update from.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldDependencies\",\n value: function updateFieldDependencies(field, data, updateParents) {\n // Recursively update parent field values\n if (updateParents) {\n this.updateFieldAncestors(field, data);\n } // Update fields listed as dependencies\n\n\n this.updateFields(this.getFieldDependencies(field), data);\n }\n /**\n * Update the ancestors of the given field.\n *\n * @protected\n * @param {Field} field - The field to update parents of.\n * @param {Object} data - The data to update with.\n */\n\n }, {\n key: \"updateFieldAncestors\",\n value: function updateFieldAncestors(field, data) {\n var i,\n parents = this.getFieldAncestors(field);\n\n for (i = 0; i < parents.length; i++) {\n this.updateFieldValue(parents[i], data);\n }\n }\n /**\n * Update a field's inheritance.\n *\n * Ensures that inherited child fields exist.\n *\n * @param {Field} field - The field to update the inheritance of.\n * @param {Object} data - The date to update with.\n */\n\n }, {\n key: \"updateFieldInheritance\",\n value: function updateFieldInheritance(field, data) {\n // Update child template fields\n this.updateTemplateFields(field, data); // Inherit the field's template\n\n this.inheritTemplate(field, data);\n }\n /**\n * Get the keys of child fields that don't exist in the given data.\n *\n * Retrieves the new keys, existing keys and old keys of a field compared\n * to its data.\n *\n * @protected\n * @param {Field} field - The field with a template.\n * @param {Object} data - The data to diff against.\n * @return array [newPaths[], existingPaths[], oldPaths[]]\n */\n\n }, {\n key: \"diffFieldDataKeys\",\n value: function diffFieldDataKeys(field, data) {\n // Grab the data keys and child field keys\n var childData = this.getFieldValue(field, data);\n var childDataKeys = childData ? Object.keys(childData) : [];\n var childFieldKeys = this.getFieldChildrenKeys(field); // Keys in data that aren't in fields\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childDataKeys, childFieldKeys); // Keys in data and fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(childDataKeys, childFieldKeys); // Keys in fields that aren't in data\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childFieldKeys, childDataKeys); // console.log('diffTemplateFieldKeys()', field.path, 'newKeys', newKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'existingKeys', existingKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'oldKeys', oldKeys);\n\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Unravel all templates into fields for the given field and data.\n *\n * TODO: Try to merge this into inheritTemplate(), or extract a method\n * that can handle both cases, like updateFieldChildrenInheritance().\n *\n * @protected\n * @param {Field} field - The field to update template fields for.\n * @param {Object} [data] - The data used to unravel field templates.\n */\n\n }, {\n key: \"updateTemplateFields\",\n value: function updateTemplateFields(field, data) {\n if (!field) {\n return;\n }\n\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return;\n }\n\n var dictionary = this.dictionary,\n i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Find child fields that need to be added, updated or removed\n\n var _this$diffFieldDataKe = this.diffFieldDataKeys(field, data),\n _this$diffFieldDataKe2 = _slicedToArray(_this$diffFieldDataKe, 3),\n newKeys = _this$diffFieldDataKe2[0],\n existingKeys = _this$diffFieldDataKe2[1],\n oldKeys = _this$diffFieldDataKe2[2];\n\n var existingFieldKeys = this.getFieldChildrenKeys(field);\n var fixedKeys = field.fixed || []; // TODO: Extract to... diffFixedFieldDataKeys()...?\n // Remove fixed keys from the keys that need removing\n\n oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(oldKeys, fixedKeys); // Add the existent fixed keys to the keys that need updating\n\n existingKeys = existingKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(fixedKeys, existingFieldKeys), existingKeys, newKeys)); // Add the non-existent fixed keys to the keys that need creating\n\n newKeys = newKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(fixedKeys, existingFieldKeys, newKeys)); //console.log(field.path, newKeys, existingKeys, oldKeys, fixedKeys, existingFieldKeys);\n // Remove old fields\n\n for (i = 0; i < oldKeys.length; i++) {\n key = oldKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n this.removeField(this.getField(path));\n } // Update existing fields\n\n\n for (i = 0; i < existingKeys.length; i++) {\n key = existingKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() existingField', field.path, path);\n // Ensure the existing field has the correct template\n\n existingField = this.getField(path);\n existingField.extends = template.path;\n } // Build new fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field);\n\n for (i = 0; i < newKeys.length; i++) {\n key = newKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() newField', path, value[key]);\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: template.path\n };\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields);\n }\n\n this.updateDictionary(newFields);\n }\n /**\n * Diff the keys of the first field's children with those of the second\n * field's children.\n *\n * Finds keys of the first that aren't of the second, keys that are of both,\n * and keys of the second that aren't of the first.\n *\n * @protected\n * @param {Field} firstField\n * @param {Field} secondField\n * @returns {array} [newKeys, existingKeys, oldKeys]\n */\n\n }, {\n key: \"diffFieldChildrenKeys\",\n value: function diffFieldChildrenKeys(firstField, secondField) {\n var firstFieldKeys = this.getFieldChildrenKeys(firstField);\n var secondFieldKeys = this.getFieldChildrenKeys(secondField); // Child keys of the first field that aren't of the second field\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(firstFieldKeys, secondFieldKeys); // Keys in the children of both fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(firstFieldKeys, secondFieldKeys); // Child keys of the second field that aren't of the first\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(secondFieldKeys, firstFieldKeys);\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Apply a field's inheritance.\n *\n * Ensures that a field inherits from its base field.\n *\n * @param {Field} field - The inheriting field.\n * @param {Object} data - The data to update with.\n * @return {Field}\n */\n\n }, {\n key: \"inheritTemplate\",\n value: function inheritTemplate(field, data) {\n if (!field) {\n return field;\n }\n\n var template = this.getFieldTemplate(field); // Skip fields without a template\n\n if (!template) {\n return field;\n } // Inherit the template\n\n\n if (field.extended !== template.path) {\n var templateClone = lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(template); // We don't want to inherit children, nor do we support more than a\n // single layer of inheritance\n\n delete templateClone.children;\n delete templateClone.template; // if (field.path === 'skills.list.test.ability') {\n // \tconsole.log('skills.list.test.ability', clone(field), templateClone);\n // }\n // Merge sandwich to retain original values\n\n field = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(field, templateClone, lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(field));\n\n if (field.name == null) {\n field.name = this.deriveName(field);\n }\n\n field.extended = template.path;\n this.prepareFields([field]);\n } //console.log('inheritTemplate()', field.path, template.path);\n // Update child field inheritance\n\n\n var i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Diff field child keys and template child keys\n\n var _this$diffFieldChildr = this.diffFieldChildrenKeys(template, field),\n _this$diffFieldChildr2 = _slicedToArray(_this$diffFieldChildr, 2),\n newKeys = _this$diffFieldChildr2[0],\n existingKeys = _this$diffFieldChildr2[1]; // Update existing template fields\n\n\n for (var _i2 = 0; _i2 < existingKeys.length; _i2++) {\n key = existingKeys[_i2];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n existingField = this.getField(path); //console.log('inheritTemplate() existingField', path, existingField);\n\n existingField.extends = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key);\n } // Build new template fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field); //console.log('inheritTemplate()', field.path, value);\n\n for (var _i3 = 0; _i3 < newKeys.length; _i3++) {\n key = newKeys[_i3];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('inherit newKey', path, value[key]);\n // Build the new field\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key)\n }; // Propagate values\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n // TODO: Extract addFieldChildren(field, children)\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields); // We then want to sort these in the order of template's children\n // TODO: Extract sortFieldChildren(field)\n\n var templateChildKeys = this.getFieldChildrenKeys(template); // We flip the child keys into an object where the values are the\n // indices of the child keys array\n\n templateChildKeys = lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default()(templateChildKeys, _toConsumableArray(templateChildKeys.keys())); // This makes it easier to sort\n\n field.children = lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default()(field.children, function (child) {\n return templateChildKeys[child.pathFragment];\n });\n }\n\n this.updateDictionary(newFields);\n return field;\n }\n /**\n * Remove data from the given path.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path to remove.\n */\n\n }, {\n key: \"removeValue\",\n value: function removeValue(data, path) {\n this.removePath(path, data);\n }\n /**\n * Remove data at the given path and update parent fields.\n *\n * TODO: The naming and existence of this method doesn't quite make sense.\n * You'd expect it to remove the field(s) too.\n * Refactor!\n *\n * @protected\n * @param {string} path - The path to remove.\n * @param {Object} data - The data to remove the path from.\n */\n\n }, {\n key: \"removePath\",\n value: function removePath(path, data) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Remove the state data\n\n\n this.removeData(field, data); // Update the parent field\n\n this.updateField(this.getFieldParent(field), data);\n }\n /**\n * Remove the given fields.\n *\n * @protected\n * @param {Field[]} fields - The fields to remove.\n */\n\n }, {\n key: \"removeFields\",\n value: function removeFields(fields) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.removeField(fields[i]);\n }\n }\n /**\n * Remove a field.\n *\n * Clears dictionary and parent references to the field.\n *\n * Doesn't remove data or update parent field values.\n *\n * @protected\n * @param {Field} field - The field to remove.\n */\n\n }, {\n key: \"removeField\",\n value: function removeField(field) {\n if (!field) {\n return;\n }\n\n this.clearValueCache(field.path); // Remove the field's children\n\n this.removeFields(field.children); // Remove the field from the dictionary and dependency list\n\n delete this.dictionary[field.path];\n delete this.fieldDependencies[field.path]; // Remove the field from its parent\n\n var parent = this.getFieldParent(field);\n lodash_pull__WEBPACK_IMPORTED_MODULE_9___default()(parent.children, field);\n }\n /**\n * Remove a field's path from the given data.\n *\n * @param {Field} field - The field whose path to remove from data.\n * @param {Object} data - The data to remove from.\n */\n\n }, {\n key: \"removeData\",\n value: function removeData(field, data) {\n if (!field) {\n return;\n }\n\n var parent = this.getField(field.parent);\n\n if (!parent) {\n return;\n }\n\n var parentPath = parent.path;\n var parentValue = this.getFieldValue(parent, data);\n var key = field.pathFragment;\n\n if (Array.isArray(parentValue)) {\n parentValue.splice(key, 1);\n } else {\n delete parentValue[key];\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, parentPath, parentValue);\n }\n /**\n * Update the dictionary with the given fields.\n *\n * @param {Field[]} fields\n * @returns {FieldDictionary}\n */\n\n }, {\n key: \"updateDictionary\",\n value: function updateDictionary(fields) {\n if (!Array.isArray(fields)) {\n return this.dictionary;\n }\n\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i] || !fields[i].path) {\n continue;\n }\n\n this.dictionary[fields[i].path] = fields[i];\n }\n\n return this.dictionary;\n }\n /**\n * Build a dictionary from the given fields.\n *\n * @param {Field[]} fields\n * @return {FieldDictionary}\n */\n\n }, {\n key: \"buildDictionary\",\n value: function buildDictionary(fields) {\n return Object(_functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__[\"default\"])(fields);\n }\n /**\n * Build a tree from the given dictionary.\n *\n * @protected\n * @param {FieldDictionary} dictionary\n * @returns {Field}\n */\n\n }, {\n key: \"buildTree\",\n value: function buildTree(dictionary) {\n return Object(_functions_buildTree__WEBPACK_IMPORTED_MODULE_22__[\"default\"])(dictionary);\n }\n /**\n * Build data from the current field state.\n *\n * @param {Field} field - The root field to traverse from.\n * @param {Object} [data={}] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildData\",\n value: function buildData(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (!field) {\n return data;\n }\n\n var child, childData; // If the field has children, build the data of its children\n\n if (field.children) {\n for (var c = 0; c < field.children.length; c++) {\n child = field.children[c];\n\n if (child.virtual || child.omit) {\n continue;\n }\n\n childData = this.buildData(field.children[c], data);\n }\n\n return data;\n } // Set data\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n return data;\n }\n /**\n * Build data for a field's template.\n *\n * @protected\n * @param {Field} field - The field to build child data for.\n * @param {Object} [data] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildTemplateData\",\n value: function buildTemplateData(field, data) {\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return null;\n }\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(this.buildData(template), template.path);\n }\n /**\n * Add new child data for the field at the given path using its template.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path of the field to add new child data to.\n * @param {string|number} [key] - Optional key to use for the new child data.\n */\n\n }, {\n key: \"addItem\",\n value: function addItem(data, path, key) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Build the new child data\n\n\n var newData = this.buildTemplateData(field); // Get the target for the data\n\n var target = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path, []); // Add the new child data to the collection\n\n if (Array.isArray(target)) {\n target.push(newData);\n } else if (key != null && _typeof(target) === 'object') {\n target[key] = newData;\n } else {\n // Bail if we're not dealing with a collection\n console.warn(\"Could not create new child data for '\".concat(path, \"';\") + \" either it wasn't an array or wasn't an object with a key provided\");\n return;\n } // Set it back\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, target); // Update the form\n\n this.updatePath(path, data);\n }\n }]);\n\n return FormProcessor;\n}();\n/**\n * A dictionary of fields.\n *\n * Fields are keyed by their path.\n *\n * @typedef {Object.} FieldDictionary\n */\n\n/**\n * A field description.\n *\n * TODO: Update this to reflect the simplest approach to describing fields.\n * Could also be called FieldOptions if passed to the constructor of a\n * Field class.\n *\n * @typedef {Object} FieldDescription\n */\n\n/**\n * A field.\n *\n * TODO: Formalise as a class?\n *\n * @typedef {Object} Field\n *\n * @property {string} path - The path of the field.\n * @property {string} [parent] - The path of the field's parent, if any. Overrides the parent that would otherwise be determined from the `path`.\n * @property {string} [pathFragment] - The leaf of the field's path. TODO: Rename to key\n * @property {string} [type] - The type of the field. Determines the type of value to read and store. Defaults to `'number'`.\n * @property {string} [input] - The input type to use for this field, if any. // TODO: Rename? Might not be an actual input... (i.e. section). `element` might be a good name.\n * @property {Object} [options] - The input options. A free-form object for different input types to interpret and utilise.\n * @property {string} [name] - The field's name. Defaults to a sentence-case translation of the field's key. TODO: Rename to label?\n * @property {string} [description] - The field's description.\n * @property {boolean} [omit=false] - Whether to prevent storing the property's value in data AND prevent updating any children. Defaults to `false`.\n * @property {boolean} [virtual=false] - Whether to prevent storing the property's value in data. Defaults to `false`.\n * @property {string|boolean} [visible=true] - Whether the property is visible. Defaults to `true`. String values are interpreted as expressions.\n * @property {string|boolean} [disabled=false] - Whether the property is disabled. Defaults to `true` if `expression` is set, otherwise defaults to `false`. String values are interpreted as expressions. TODO: Input options?\n * @property {*} [value] - The field's value.\n * @property {*} [default] - The field's default value. Defaults appropriately for the set `type`.\n * @property {boolean} [merge] - Whether to merge the field's non-scalar value with its default value.\n * @property {string} [expression] - An expression used to compute the field's value. Implies `disabled` when set.\n * @property {string} [validator] - The field's validation function. Defaults as appropriate to the `type`.\n * @property {string} [extends] - The path of a field to inherit.\n * @property {string} [extended] - The path of a field that been inherited.\n * @property {string} [mirror] - The path of a field to mirror. TODO: Implement\n * @property {Field[]} [children] - Child fields.\n * @property {string} [template] - Template field that all child fields should extend. Can be a `Field` or a `path` to a field.\n * @property {Object|array} [fixed] - A map or list of child keys that cannot be removed at runtime, if present.\n */\n\n\n\n\n//# sourceURL=webpack://%5Bname%5D/./src/services/FormProcessor.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return FormProcessor; });\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! expr-eval */ \"./node_modules/expr-eval/dist/bundle.js\");\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(expr_eval__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! lodash/clone */ \"./node_modules/lodash/clone.js\");\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lodash_clone__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! lodash/merge */ \"./node_modules/lodash/merge.js\");\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! lodash/defaultsDeep */ \"./node_modules/lodash/defaultsDeep.js\");\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! lodash/map */ \"./node_modules/lodash/map.js\");\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(lodash_map__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! lodash/reduce */ \"./node_modules/lodash/reduce.js\");\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(lodash_reduce__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! lodash/has */ \"./node_modules/lodash/has.js\");\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(lodash_has__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/get */ \"./node_modules/lodash/get.js\");\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_get__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! lodash/set */ \"./node_modules/lodash/set.js\");\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(lodash_set__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! lodash/pull */ \"./node_modules/lodash/pull.js\");\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(lodash_pull__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! lodash/defaultTo */ \"./node_modules/lodash/defaultTo.js\");\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! lodash/difference */ \"./node_modules/lodash/difference.js\");\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(lodash_difference__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! lodash/intersection */ \"./node_modules/lodash/intersection.js\");\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(lodash_intersection__WEBPACK_IMPORTED_MODULE_12__);\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lodash/isPlainObject */ \"./node_modules/lodash/isPlainObject.js\");\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__);\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! lodash/toNumber */ \"./node_modules/lodash/toNumber.js\");\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__);\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! lodash/zipObject */ \"./node_modules/lodash/zipObject.js\");\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__);\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! lodash/sortBy */ \"./node_modules/lodash/sortBy.js\");\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__);\n/* harmony import */ var _mixins_util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../mixins/util */ \"./src/mixins/util.js\");\n/* harmony import */ var _functions_sum__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../functions/sum */ \"./src/functions/sum.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! lodash/sumBy */ \"./node_modules/lodash/sumBy.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default = /*#__PURE__*/__webpack_require__.n(lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__);\n/* harmony import */ var _functions_multiply__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../functions/multiply */ \"./src/functions/multiply.js\");\n/* harmony import */ var _functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../functions/buildDictionary */ \"./src/functions/buildDictionary.js\");\n/* harmony import */ var _functions_buildTree__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../functions/buildTree */ \"./src/functions/buildTree.js\");\n/* harmony import */ var _functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../functions/traverseTree */ \"./src/functions/traverseTree.js\");\n/* harmony import */ var _functions_splitPath__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../functions/splitPath */ \"./src/functions/splitPath.js\");\n/* harmony import */ var _functions_joinPath__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../functions/joinPath */ \"./src/functions/joinPath.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! flat */ \"./node_modules/flat/index.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(flat__WEBPACK_IMPORTED_MODULE_26__);\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! deep-object-diff */ \"./node_modules/deep-object-diff/dist/index.js\");\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27___default = /*#__PURE__*/__webpack_require__.n(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); }\n\nfunction _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar UP = -1;\nvar BOTH = 0;\nvar DOWN = 1;\n/**\n * Pragma form.\n *\n * Expands field lists a dictionary and tree. Processes field expressions from\n * state data.\n *\n * TODO: Rename to Form?\n *\n * @class FormProcessor\n */\n\nvar FormProcessor =\n/*#__PURE__*/\nfunction () {\n /**\n * Create a new property processor.\n *\n * @constructor\n * @param {Field[]} [fields=[]] - Initial form fields.\n * @param {Object.} [functions={}] - Functions to make available for field expressions.\n * @param {Object.} [inputOptions={}] - Default input options keyed by input type.\n */\n function FormProcessor() {\n var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var functions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var inputOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n _classCallCheck(this, FormProcessor);\n\n /**\n * Typecasting functions for each field type.\n *\n * TODO: Strong casting functions\n *\n * @type {Object.}\n */\n this.casts = {\n 'string': function string(f, v) {\n return v == null ? '' : '' + v;\n },\n 'number': function number(f, v) {\n return lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default()(_mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].clamp(v, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.min'), lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.max')));\n },\n 'boolean': function boolean(f, v) {\n return !!v;\n }\n };\n /**\n * Default property values for each field type.\n *\n * @type {Object}\n */\n\n this.defaults = {};\n this.setDefaults({\n '*': {\n type: 'number',\n visible: true\n },\n 'virtual': {\n visible: false,\n virtual: true,\n omit: true\n },\n 'string': {\n input: 'string',\n default: ''\n },\n 'number': {\n input: 'number',\n default: 0,\n options: {\n min: -100,\n max: 100,\n step: 1\n }\n },\n 'boolean': {\n input: 'boolean',\n default: false\n },\n 'selection': {\n input: 'selection',\n options: {\n options: {}\n }\n },\n 'section': {\n input: 'section'\n },\n 'group': {\n input: 'group'\n },\n 'list': {\n input: 'list'\n },\n 'list-item': {\n input: 'list-item'\n },\n 'table': {\n input: 'pragma-table'\n }\n });\n /**\n * Default input options for each input type.\n *\n * @type {Object.}\n */\n\n this.inputOptions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, inputOptions);\n /**\n * Expression functions.\n *\n * @type {Object.}\n */\n\n this.functions = {};\n /**\n * Expression parser.\n *\n * @type {Parser}\n */\n\n this.parser = new expr_eval__WEBPACK_IMPORTED_MODULE_0___default.a.Parser({\n operators: {\n in: true\n }\n }); // Set the functions\n\n this.addFunctions(lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({\n concat: function concat() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return args.join('');\n },\n keys: Object.keys,\n multiply: _functions_multiply__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n sum: _functions_sum__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n sumBy: lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default.a,\n map: lodash_map__WEBPACK_IMPORTED_MODULE_4___default.a,\n reduce: lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default.a\n }, functions));\n /**\n * The set of form fields.\n *\n * @type {Field[]}\n */\n\n this.fields = [];\n /**\n * Fields keyed by path.\n *\n * @type {FieldDictionary}\n */\n\n this.dictionary = {};\n /**\n * The root node of the field tree.\n *\n * @type {Field}\n */\n\n this.tree = {}; // Set the form fields\n\n this.setFields(fields);\n /**\n * Value cache for each field.\n *\n * @type {Object.}\n */\n\n this.valueCache = {};\n /**\n * Field expression cache keyed by path.\n *\n * @type {Object.}\n */\n\n this.expressionCache = {};\n /**\n * Field update dependencies keyed by path.\n *\n * @type {Object.}\n */\n\n this.fieldDependencies = {};\n /**\n * Map of updated fields.\n *\n * Used to prevent updating fields more than once.\n *\n * @type {Object.}\n */\n\n this.updatedFields = {};\n }\n /**\n * Check whether a field exists at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to check.\n * @return {boolean}\n */\n\n\n _createClass(FormProcessor, [{\n key: \"hasField\",\n value: function hasField(path) {\n return lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(this.dictionary, path);\n }\n /**\n * Get the field at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to get.\n * @return {Field}\n */\n\n }, {\n key: \"getField\",\n value: function getField(path) {\n return this.dictionary[path];\n }\n /**\n * Get the parent field of the field at the given path.\n *\n * @protected\n * @param {Field} field\n * @return {Field|null}\n */\n\n }, {\n key: \"getFieldParent\",\n value: function getFieldParent(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.parent);\n }\n /**\n * Get the ancestors of a field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]}\n */\n\n }, {\n key: \"getFieldAncestors\",\n value: function getFieldAncestors(field) {\n var ancestors = [];\n\n while (field.hasOwnProperty('parent') && this.hasField(field.parent)) {\n field = this.getFieldParent(field);\n ancestors.push(field);\n }\n\n return ancestors;\n }\n /**\n * Get the current value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the value of.\n * @param {*} [data={}] - Optional data to read current values from.\n * @param {*} [value] - Optional current value.\n * @return {*} The current value of the field.\n */\n\n }, {\n key: \"getFieldValue\",\n value: function getFieldValue(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n\n if (!field) {\n return value;\n }\n\n value = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path)); // Merge default values if specified\n\n if (field.merge) {\n if (lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default()(field.default)) {\n return lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, field.default, field.value, value);\n } // TODO: Handle arrays\n\n } // Otherwise use the first defined value\n\n\n return lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n }\n /**\n * Get the default value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the default value of.\n * @return {*} The default value of the field.\n */\n\n }, {\n key: \"getFieldDefaultValue\",\n value: function getFieldDefaultValue(field) {\n if (!field) {\n return null;\n }\n\n return field.default;\n }\n /**\n * Get the template field that a field should a extend.\n *\n * @param {Field} field - The field to get the template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldTemplate\",\n value: function getFieldTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.extends);\n }\n /**\n * Get the keys of the field's children.\n *\n * @protected\n * @param {Field} field - The field to get the child keys of.\n * @return {array} The keys of the field's children.\n */\n\n }, {\n key: \"getFieldChildrenKeys\",\n value: function getFieldChildrenKeys(field) {\n var i,\n keys = [],\n children = field.children || [];\n\n for (i = 0; i < children.length; i++) {\n keys.push(children[i].pathFragment);\n }\n\n return keys;\n }\n /**\n * Get the template that a field's children should extend.\n *\n * @protected\n * @param {Field} field - The field to get the child template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldChildrenTemplate\",\n value: function getFieldChildrenTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.template);\n }\n /**\n * Get the fields dependent upon the given field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]} The dependent fields\n */\n\n }, {\n key: \"getFieldDependencies\",\n value: function getFieldDependencies(field) {\n var dependencies = this.fieldDependencies[field.path]; // Skip if the field has no dependencies\n\n if (!dependencies || !dependencies.length) {\n return [];\n }\n\n var fields = [];\n\n for (var i = 0; i < dependencies.length; i++) {\n var dependency = this.getField(dependencies[i]);\n\n if (!dependency) {\n continue;\n }\n\n fields.push(dependency);\n }\n\n return fields;\n }\n /**\n * Get the current value of a field.\n *\n * Falls back to default values as appropriate.\n *\n * @public\n * @param {string} path - The path to the field.\n * @return {*} The value of the field\n */\n\n }, {\n key: \"getValue\",\n value: function getValue(path) {\n return this.getFieldValue(this.getField(path));\n }\n /**\n * Cast a value based on the property it belongs to.\n *\n * @public\n * @param {Field} field\n * @param {*} value\n */\n\n }, {\n key: \"castValue\",\n value: function castValue(field, value) {\n if (!field) return value;\n if (!this.casts[field.type]) return value; // if (Array.isArray(value))\n // \treturn value.map(this.casts[field.type]);\n\n value = this.casts[field.type](field, value);\n return value;\n }\n /**\n * Derive a field's value from some data.\n *\n * @protected\n * @param {string} path - The path of the field to derive a value for.\n * @param {Object} data - The data to derive values from.\n * @return {*} The derived value.\n */\n\n }, {\n key: \"deriveValue\",\n value: function deriveValue(path, data) {\n // Return from the value cache if a value is set\n if (this.valueCache.hasOwnProperty(path)) {\n return this.valueCache[path];\n }\n\n var field = this.getField(path);\n var value = this.getFieldValue(field, data); // Return the raw value if there's no such field\n\n if (!field) {\n return value;\n } // Cast the value\n\n\n value = this.castValue(field, value); // Evaluate the field's expression\n\n value = this.evaluateFieldExpression(field, data, value); // Fall back to defaults\n //value = defaultTo(value, defaultTo(field.default, null));\n\n value = this.getFieldValue(field, data, value); // Update the value cache\n\n this.valueCache[path] = value;\n return value;\n }\n /**\n * Build a field's expression.\n *\n * @param {Field} field - The field to build an expression for.\n * @return {Expression} The built expression.\n */\n\n }, {\n key: \"buildFieldExpression\",\n value: function buildFieldExpression(field) {\n if (field.expression == null || typeof field.expression !== 'string') {\n return null;\n } // Use the cached expression if one is available\n\n\n if (this.expressionCache[field.path]) {\n return this.expressionCache[field.path];\n } // Build the initial expression\n\n\n var expression;\n\n try {\n expression = this.parser.parse(field.expression);\n } catch (error) {\n console.error(\"Error parsing expression for field '\".concat(field.path, \"': \").concat(error.message));\n return null;\n } // Substitute contextual variables\n\n\n var substitutions = {\n $parent: field.parent\n };\n\n for (var s in substitutions) {\n try {\n expression = expression.substitute(s, substitutions[s]);\n } catch (error) {\n console.error(\"Error substituting expression variable '\".concat(s, \"' for field '\").concat(field.path, \": \").concat(error.message));\n return null;\n }\n }\n\n this.expressionCache[field.path] = expression;\n return expression;\n }\n /**\n * Evaluate a field's value from its expression.\n *\n * Causes the evaluation of any field dependencies as a result.\n *\n * TODO: Evaluate (and cache) expressions for other field properties! :D\n *\n * @param {Field} field - The field to compute the value of.\n * @param {Object} data - The data to derive values from.\n * @param {*} [value] - The current value of the field.\n * @return {*} The computed value of the field's expression.\n */\n\n }, {\n key: \"evaluateFieldExpression\",\n value: function evaluateFieldExpression(field, data, value) {\n var _this = this;\n\n value = this.getFieldValue(field, data, value); // Parse the expression\n\n var expression = this.buildFieldExpression(field);\n\n if (!expression) {\n return value;\n } // TODO: Extract deriving variables and building contextual functions\n // let variables = buildExpressionContext(field, data, expression, value)?\n // Contextual functions should still update the same \"variables\" reference\n // Derive values for the variables in the expression\n\n\n var variables = expression.variables({\n withMembers: true\n });\n var values = {\n $this: field,\n $value: value\n };\n\n for (var v = 0; v < variables.length; v++) {\n var variable = variables[v];\n if (lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(values, variable)) continue;\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(values, variable, this.deriveValue(variable, data));\n } // Build contextual functions\n\n\n values = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(values, {\n field: function field(path) {\n variables.push(path);\n return _this.getField(path);\n },\n value: function value(path) {\n // Add the path to the list of expression variables\n variables.push(path); // Derive the value for the expression\n\n return _this.deriveValue(path, data);\n }\n }); // Evaluate the expression\n\n try {\n value = expression.evaluate(values);\n } catch (error) {\n console.log('evaluateFieldExpression', field, data, value);\n console.error(\"Error evaluating expression for field '\".concat(field.path, \"': \").concat(error.message));\n } //console.log('evaluateFieldExpression', field.path, expression.toString(), variables, values, value);\n //console.log('evaluateFieldExpression expression', expression);\n // Update the map of field update dependencies\n // TODO: Exclude contextual variables\n // TODO: Move this to an earlier processing step that evaluates the\n // expression with spy functions\n\n\n for (var _v = 0; _v < variables.length; _v++) {\n var _variable = variables[_v];\n this.fieldDependencies[_variable] = this.fieldDependencies[_variable] || [];\n\n if (this.fieldDependencies[_variable].indexOf(field.path) < 0) {\n this.fieldDependencies[_variable].push(field.path);\n }\n }\n\n return value;\n }\n /**\n * Prepare fields from a set of field descriptions.\n *\n * Ascertain's the parent path and path fragment (key) of each field.\n *\n * TODO: Field class, FieldDescription typedef.\n *\n * @protected\n * @param {Field[]} fields - The field description.\n * @returns {Field[]} The given fields prepared with pathFragment and parent properties.\n */\n\n }, {\n key: \"prepareFields\",\n value: function prepareFields(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field, pathFragment, parentPath;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Ascertain a parent path and path fragment\n\n var _splitPath = Object(_functions_splitPath__WEBPACK_IMPORTED_MODULE_24__[\"default\"])(field.path);\n\n var _splitPath2 = _slicedToArray(_splitPath, 2);\n\n parentPath = _splitPath2[0];\n pathFragment = _splitPath2[1];\n field.pathFragment = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.pathFragment, pathFragment);\n field.parent = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.parent, parentPath);\n }\n\n return fields;\n }\n /**\n * Set the form's fields.\n *\n * TODO: Creates, updates and removes fields accordingly.\n *\n * @public\n * @param {Field[]} fields\n */\n\n }, {\n key: \"setFields\",\n value: function setFields(fields) {\n // Prepare the fields\n this.fields = this.prepareFields(fields); // TODO: Build dictionary from given fields, compare with current fields\n //this.dictionary = this.buildDictionary(this.fields);\n\n this.dictionary = this.updateDictionary(this.fields); // Compose the fields into a tree\n\n this.tree = this.buildTree(this.dictionary); // Clear all caches\n\n this.valueCache = {}; //this.expressionCache = {};\n\n this.fieldDependencies = {};\n }\n /**\n * Add a form field.\n *\n * @param {Field} field\n */\n\n }, {\n key: \"addField\",\n value: function addField(field) {\n var parent = this.getFieldParent(field);\n\n if (!parent) {\n console.warn(\"Could not set field \".concat(field.path, \" - its parent does not exist\"));\n return;\n }\n\n if (!parent.children) {\n parent.children = [];\n }\n\n if (!parent.children.includes(field)) {\n parent.children.push(field);\n }\n\n this.dictionary[field.path] = field;\n }\n /**\n * Update a property with the given value.\n *\n * @public\n * @param {Object} data - The data to update.\n * @param {string} path - The path of the field to update.\n * @param {*} value - The value to set.\n * @return {*} The updated value\n */\n\n }, {\n key: \"setValue\",\n value: function setValue(data, path, value) {\n var field = this.getField(path); // Update the value if one is given\n\n if (value !== undefined) {\n if (field) {\n field.value = value;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, value);\n } // Update the field at this path\n\n\n this.updatePath(path, data); // Get the updated value\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path);\n }\n /**\n * Clear the value cache.\n *\n * Optionally accepts a path to clear.\n *\n * @param {string} path\n */\n\n }, {\n key: \"clearValueCache\",\n value: function clearValueCache() {\n var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n\n // Just empty the entire cache if there's no path is or there's no field\n // at the given path\n if (!path || !this.getField(path)) {\n this.valueCache = {};\n return;\n }\n\n var i,\n field = this.getField(path); // Clear cached values of child fields iteratively\n\n var child,\n children = field.children,\n nextChildren = [];\n\n while (children && children.length) {\n nextChildren = [];\n\n for (i = 0; i < children.length; i++) {\n child = children[i];\n delete this.valueCache[child.path];\n\n if (child.children) {\n nextChildren = nextChildren.concat(child.children);\n }\n }\n\n children = nextChildren;\n } // Clear the cached value for this field\n\n\n delete this.valueCache[field.path]; // Clear cached values of parent fields iteratively\n\n var ancestors = this.getFieldAncestors(field);\n\n for (i = 0; i < ancestors.length; i++) {\n delete this.valueCache[ancestors[i].path];\n }\n }\n /**\n * Derive a property's name from its path.\n *\n * @protected\n * @param {Field} field\n * @return {string} The derived name\n */\n\n }, {\n key: \"deriveName\",\n value: function deriveName(field) {\n var path = field.path;\n var lastDotIndex = path.lastIndexOf('.');\n return _mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].sentenceCase(path.substring(lastDotIndex + 1));\n }\n /**\n * Set the default field properties for each type.\n *\n * @param {Object.} fieldProperties - The default field properties, keyed by field type.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(fieldProperties) {\n this.defaults = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.defaults, fieldProperties);\n }\n /**\n * Apply the form's default properties to the given fields.\n *\n * Fills in default values, derives default names.\n *\n * @protected\n * @param {Field[]} fields - The fields to apply default values to.\n * @returns {Field[]}\n */\n\n }, {\n key: \"applyDefaults\",\n value: function applyDefaults(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Derive a name\n\n if (field.name === undefined) {\n field.name = this.deriveName(field);\n } // Apply global defaults\n\n\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults['*']); // Apply type-specific defaults\n\n if (this.defaults[field.type]) {\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults[field.type]);\n } // Apply default input options\n\n\n if (this.inputOptions[field.input]) {\n field.options = field.options || {};\n field.options = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field.options, this.inputOptions[field.input]);\n } // Disable the field implicitly if it has an expression\n\n\n if (!field.hasOwnProperty('disabled')) {\n field.disabled = !!field.expression;\n }\n }\n\n return fields;\n }\n /**\n * Add to the form's functions.\n *\n * @param {Object.} functions - Functions to add, keyed by name.\n */\n\n }, {\n key: \"addFunctions\",\n value: function addFunctions(functions) {\n // Add the functions to the form\n this.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.functions, functions); // Add the functions to the expression parser\n\n this.parser.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.parser.functions, this.functions); // Clear the expression cache\n //this.expressionCache = {};\n }\n /**\n * Update the form using the given data.\n *\n * @public\n * @param {Object} [data] - The data to update with.\n */\n\n }, {\n key: \"update\",\n value: function update(data) {\n // Clear updated fields map\n this.updatedFields = {};\n console.time('buildData');\n var formData = this.buildData(this.tree);\n console.timeEnd('buildData');\n console.log(formData);\n console.time('diff');\n var diff = Object(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__[\"detailedDiff\"])(formData, data);\n console.timeEnd('diff');\n console.log(diff);\n console.time('flatten');\n var addedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.added);\n var updatedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.updated);\n var deletedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.deleted);\n console.timeEnd('flatten');\n console.log(addedPaths, updatedPaths, deletedPaths); //let path;\n // for (path in addedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // for (path in updatedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n //\n // for (path in deletedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // Update the value of every field\n\n this.updatePath('', data);\n console.time('traverseTree');\n Object(_functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__[\"default\"])(this.tree, function (field) {// Pre-order\n }, function (field) {// Post-order\n //console.log(field.path);\n });\n console.timeEnd('traverseTree');\n }\n /**\n * Update the field at the given path.\n *\n * @param {string} path - The path of the field to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updatePath\",\n value: function updatePath(path, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n this.updateField(this.getField(path), data, direction);\n }\n /**\n * Update the given fields with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field[]} fields - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateFields\",\n value: function updateFields(fields, data, direction) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.updateField(fields[i], data, direction);\n }\n }\n /**\n * Update the given field with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field} field - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateField\",\n value: function updateField(field, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n\n if (!field) {\n return;\n } //console.log('updateField()', field.path);\n //console.count('updateField()');\n\n\n this.clearValueCache(field.path); // Update the field's children\n\n if (direction >= 0) {\n // TODO: This would be a good spot for an event to fire to allow plugins\n // (like inheritance) to intercept child update behaviour\n this.updateFieldInheritance(field, data);\n\n if (!field.omit) {\n this.updateFields(field.children, data);\n }\n } // Skip if this field has already been updated\n\n\n if (this.updatedFields[field.path]) {\n return;\n } // Apply default values\n\n\n this.applyDefaults([field]); // Update the state's value\n\n this.updateDataValue(field, data); // Update the field's value (and update all fields dependent on this one)\n\n this.updateFieldValue(field, data, direction <= 0); // Mark the field as updated\n\n this.updatedFields[field.path] = true;\n }\n /**\n * Update data from the given field.\n *\n * @protected\n * @param {Field} field - The field to update with.\n * @param {Object} data - The data to update.\n * @return {Object} The updated data.\n */\n\n }, {\n key: \"updateDataValue\",\n value: function updateDataValue(field, data) {\n if (!field || field.omit || field.virtual) {\n return data;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, this.deriveValue(field.path, data));\n return data;\n }\n /**\n * Update a field using the given data.\n *\n * @protected\n * @param {Field} field - The field to update.\n * @param {Object} data - The data to update with.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldValue\",\n value: function updateFieldValue(field, data) {\n var updateParents = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!field) {\n return;\n } // Update the field value\n\n\n field.value = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path); // Update all fields dependent on this one\n\n this.updateFieldDependencies(field, data, updateParents);\n }\n /**\n * Update fields that are dependent upon the value of the given field.\n *\n * @protected\n * @param {Field} field - The field to update dependencies of.\n * @param {Object} data - The data to update from.\n * @param {boolean=false} updateAncestors - Whether to update the field's ancestors.\n */\n\n }, {\n key: \"updateFieldDependencies\",\n value: function updateFieldDependencies(field, data, updateAncestors) {\n // Recursively update parent field values\n if (updateAncestors) {\n this.updateFieldAncestorValues(field, data);\n } // Update fields listed as dependencies\n\n\n this.updateFields(this.getFieldDependencies(field), data);\n }\n /**\n * Update the ancestors of the given field.\n *\n * @protected\n * @param {Field} field - The field to update parents of.\n * @param {Object} data - The data to update with.\n */\n\n }, {\n key: \"updateFieldAncestorValues\",\n value: function updateFieldAncestorValues(field, data) {\n var i,\n parents = this.getFieldAncestors(field);\n\n for (i = 0; i < parents.length; i++) {\n this.updateFieldValue(parents[i], data);\n }\n }\n /**\n * Update a field's inheritance.\n *\n * Ensures that inherited child fields exist.\n *\n * @param {Field} field - The field to update the inheritance of.\n * @param {Object} data - The date to update with.\n */\n\n }, {\n key: \"updateFieldInheritance\",\n value: function updateFieldInheritance(field, data) {\n // Update child template fields\n this.updateTemplateFields(field, data); // Inherit the field's template\n\n this.inheritTemplate(field, data);\n }\n /**\n * Get the keys of child fields that don't exist in the given data.\n *\n * Retrieves the new keys, existing keys and old keys of a field compared\n * to its data.\n *\n * @protected\n * @param {Field} field - The field with a template.\n * @param {Object} data - The data to diff against.\n * @return array [newPaths[], existingPaths[], oldPaths[]]\n */\n\n }, {\n key: \"diffFieldDataKeys\",\n value: function diffFieldDataKeys(field, data) {\n // Grab the data keys and child field keys\n var childData = this.getFieldValue(field, data);\n var childDataKeys = childData ? Object.keys(childData) : [];\n var childFieldKeys = this.getFieldChildrenKeys(field); // Keys in data that aren't in fields\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childDataKeys, childFieldKeys); // Keys in data and fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(childDataKeys, childFieldKeys); // Keys in fields that aren't in data\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childFieldKeys, childDataKeys); // console.log('diffTemplateFieldKeys()', field.path, 'newKeys', newKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'existingKeys', existingKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'oldKeys', oldKeys);\n\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Unravel all templates into fields for the given field and data.\n *\n * TODO: Try to merge this into inheritTemplate(), or extract a method\n * that can handle both cases, like updateFieldChildrenInheritance().\n *\n * @protected\n * @param {Field} field - The field to update template fields for.\n * @param {Object} [data] - The data used to unravel field templates.\n */\n\n }, {\n key: \"updateTemplateFields\",\n value: function updateTemplateFields(field, data) {\n if (!field) {\n return;\n }\n\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return;\n }\n\n var dictionary = this.dictionary,\n i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Find child fields that need to be added, updated or removed\n\n var _this$diffFieldDataKe = this.diffFieldDataKeys(field, data),\n _this$diffFieldDataKe2 = _slicedToArray(_this$diffFieldDataKe, 3),\n newKeys = _this$diffFieldDataKe2[0],\n existingKeys = _this$diffFieldDataKe2[1],\n oldKeys = _this$diffFieldDataKe2[2];\n\n var existingFieldKeys = this.getFieldChildrenKeys(field);\n var fixedKeys = field.fixed || []; // TODO: Extract to... diffFixedFieldDataKeys()...?\n // Remove fixed keys from the keys that need removing\n\n oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(oldKeys, fixedKeys); // Add the existent fixed keys to the keys that need updating\n\n existingKeys = existingKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(fixedKeys, existingFieldKeys), existingKeys, newKeys)); // Add the non-existent fixed keys to the keys that need creating\n\n newKeys = newKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(fixedKeys, existingFieldKeys, newKeys)); //console.log(field.path, newKeys, existingKeys, oldKeys, fixedKeys, existingFieldKeys);\n // Remove old fields\n\n for (i = 0; i < oldKeys.length; i++) {\n key = oldKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n this.removeField(this.getField(path));\n } // Update existing fields\n\n\n for (i = 0; i < existingKeys.length; i++) {\n key = existingKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() existingField', field.path, path);\n // Ensure the existing field has the correct template\n\n existingField = this.getField(path);\n existingField.extends = template.path;\n } // Build new fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field);\n\n for (i = 0; i < newKeys.length; i++) {\n key = newKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() newField', path, value[key]);\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: template.path\n };\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields);\n }\n\n this.updateDictionary(newFields);\n }\n /**\n * Diff the keys of the first field's children with those of the second\n * field's children.\n *\n * Finds keys of the first that aren't of the second, keys that are of both,\n * and keys of the second that aren't of the first.\n *\n * @protected\n * @param {Field} firstField\n * @param {Field} secondField\n * @returns {array} [newKeys, existingKeys, oldKeys]\n */\n\n }, {\n key: \"diffFieldChildrenKeys\",\n value: function diffFieldChildrenKeys(firstField, secondField) {\n var firstFieldKeys = this.getFieldChildrenKeys(firstField);\n var secondFieldKeys = this.getFieldChildrenKeys(secondField); // Child keys of the first field that aren't of the second field\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(firstFieldKeys, secondFieldKeys); // Keys in the children of both fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(firstFieldKeys, secondFieldKeys); // Child keys of the second field that aren't of the first\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(secondFieldKeys, firstFieldKeys);\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Apply a field's inheritance.\n *\n * Ensures that a field inherits from its base field.\n *\n * @param {Field} field - The inheriting field.\n * @param {Object} data - The data to update with.\n * @return {Field}\n */\n\n }, {\n key: \"inheritTemplate\",\n value: function inheritTemplate(field, data) {\n if (!field) {\n return field;\n }\n\n var template = this.getFieldTemplate(field); // Skip fields without a template\n\n if (!template) {\n return field;\n } // Inherit the template\n\n\n if (field.extended !== template.path) {\n var templateClone = lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(template); // We don't want to inherit children, nor do we support more than a\n // single layer of inheritance\n\n delete templateClone.children;\n delete templateClone.template; // if (field.path === 'skills.list.test.ability') {\n // \tconsole.log('skills.list.test.ability', clone(field), templateClone);\n // }\n // Merge sandwich to retain original values\n\n field = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(field, templateClone, lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(field));\n\n if (field.name == null) {\n field.name = this.deriveName(field);\n }\n\n field.extended = template.path;\n this.prepareFields([field]);\n } //console.log('inheritTemplate()', field.path, template.path);\n // Update child field inheritance\n\n\n var i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Diff field child keys and template child keys\n\n var _this$diffFieldChildr = this.diffFieldChildrenKeys(template, field),\n _this$diffFieldChildr2 = _slicedToArray(_this$diffFieldChildr, 2),\n newKeys = _this$diffFieldChildr2[0],\n existingKeys = _this$diffFieldChildr2[1]; // Update existing template fields\n\n\n for (var _i2 = 0; _i2 < existingKeys.length; _i2++) {\n key = existingKeys[_i2];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n existingField = this.getField(path); //console.log('inheritTemplate() existingField', path, existingField);\n\n existingField.extends = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key);\n } // Build new template fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field); //console.log('inheritTemplate()', field.path, value);\n\n for (var _i3 = 0; _i3 < newKeys.length; _i3++) {\n key = newKeys[_i3];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('inherit newKey', path, value[key]);\n // Build the new field\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key)\n }; // Propagate values\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n // TODO: Extract addFieldChildren(field, children)\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields); // We then want to sort these in the order of template's children\n // TODO: Extract sortFieldChildren(field)\n\n var templateChildKeys = this.getFieldChildrenKeys(template); // We flip the child keys into an object where the values are the\n // indices of the child keys array\n\n templateChildKeys = lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default()(templateChildKeys, _toConsumableArray(templateChildKeys.keys())); // This makes it easier to sort\n\n field.children = lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default()(field.children, function (child) {\n return templateChildKeys[child.pathFragment];\n });\n }\n\n this.updateDictionary(newFields);\n return field;\n }\n /**\n * Remove data from the given path.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path to remove.\n */\n\n }, {\n key: \"removeValue\",\n value: function removeValue(data, path) {\n this.removePath(path, data);\n }\n /**\n * Remove data at the given path and update parent fields.\n *\n * TODO: The naming and existence of this method doesn't quite make sense.\n * You'd expect it to remove the field(s) too.\n * Refactor!\n *\n * @protected\n * @param {string} path - The path to remove.\n * @param {Object} data - The data to remove the path from.\n */\n\n }, {\n key: \"removePath\",\n value: function removePath(path, data) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Remove the state data\n\n\n this.removeData(field, data); // Update the parent field\n\n this.updateField(this.getFieldParent(field), data);\n }\n /**\n * Remove the given fields.\n *\n * @protected\n * @param {Field[]} fields - The fields to remove.\n */\n\n }, {\n key: \"removeFields\",\n value: function removeFields(fields) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.removeField(fields[i]);\n }\n }\n /**\n * Remove a field.\n *\n * Clears dictionary and parent references to the field.\n *\n * Doesn't remove data or update parent field values.\n *\n * @protected\n * @param {Field} field - The field to remove.\n */\n\n }, {\n key: \"removeField\",\n value: function removeField(field) {\n if (!field) {\n return;\n }\n\n this.clearValueCache(field.path); // Remove the field's children\n\n this.removeFields(field.children); // Remove the field from the dictionary and dependency list\n\n delete this.dictionary[field.path];\n delete this.fieldDependencies[field.path]; // Remove the field from its parent\n\n var parent = this.getFieldParent(field);\n lodash_pull__WEBPACK_IMPORTED_MODULE_9___default()(parent.children, field);\n }\n /**\n * Remove a field's path from the given data.\n *\n * @param {Field} field - The field whose path to remove from data.\n * @param {Object} data - The data to remove from.\n */\n\n }, {\n key: \"removeData\",\n value: function removeData(field, data) {\n if (!field) {\n return;\n }\n\n var parent = this.getField(field.parent);\n\n if (!parent) {\n return;\n }\n\n var parentPath = parent.path;\n var parentValue = this.getFieldValue(parent, data);\n var key = field.pathFragment;\n\n if (Array.isArray(parentValue)) {\n parentValue.splice(key, 1);\n } else {\n delete parentValue[key];\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, parentPath, parentValue);\n }\n /**\n * Update the dictionary with the given fields.\n *\n * @param {Field[]} fields\n * @returns {FieldDictionary}\n */\n\n }, {\n key: \"updateDictionary\",\n value: function updateDictionary(fields) {\n if (!Array.isArray(fields)) {\n return this.dictionary;\n }\n\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i] || !fields[i].path) {\n continue;\n }\n\n this.dictionary[fields[i].path] = fields[i];\n }\n\n return this.dictionary;\n }\n /**\n * Build a dictionary from the given fields.\n *\n * @param {Field[]} fields\n * @return {FieldDictionary}\n */\n\n }, {\n key: \"buildDictionary\",\n value: function buildDictionary(fields) {\n return Object(_functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__[\"default\"])(fields);\n }\n /**\n * Build a tree from the given dictionary.\n *\n * @protected\n * @param {FieldDictionary} dictionary\n * @returns {Field}\n */\n\n }, {\n key: \"buildTree\",\n value: function buildTree(dictionary) {\n return Object(_functions_buildTree__WEBPACK_IMPORTED_MODULE_22__[\"default\"])(dictionary);\n }\n /**\n * Build data from the current field state.\n *\n * @param {Field} field - The root field to traverse from.\n * @param {Object} [data={}] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildData\",\n value: function buildData(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (!field) {\n return data;\n }\n\n var child, childData; // If the field has children, build the data of its children\n\n if (field.children) {\n for (var c = 0; c < field.children.length; c++) {\n child = field.children[c];\n\n if (child.virtual || child.omit) {\n continue;\n }\n\n childData = this.buildData(field.children[c], data);\n }\n\n return data;\n } // Set data\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n return data;\n }\n /**\n * Build data for a field's template.\n *\n * @protected\n * @param {Field} field - The field to build child data for.\n * @param {Object} [data] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildTemplateData\",\n value: function buildTemplateData(field, data) {\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return null;\n }\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(this.buildData(template), template.path);\n }\n /**\n * Add new child data for the field at the given path using its template.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path of the field to add new child data to.\n * @param {string|number} [key] - Optional key to use for the new child data.\n */\n\n }, {\n key: \"addItem\",\n value: function addItem(data, path, key) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Build the new child data\n\n\n var newData = this.buildTemplateData(field); // Get the target for the data\n\n var target = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path, []); // Add the new child data to the collection\n\n if (Array.isArray(target)) {\n target.push(newData);\n } else if (key != null && _typeof(target) === 'object') {\n target[key] = newData;\n } else {\n // Bail if we're not dealing with a collection\n console.warn(\"Could not create new child data for '\".concat(path, \"';\") + \" either it wasn't an array or wasn't an object with a key provided\");\n return;\n } // Set it back\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, target); // Update the form\n\n this.updatePath(path, data);\n }\n }]);\n\n return FormProcessor;\n}();\n/**\n * A dictionary of fields.\n *\n * Fields are keyed by their path.\n *\n * @typedef {Object.} FieldDictionary\n */\n\n/**\n * A field description.\n *\n * TODO: Update this to reflect the simplest approach to describing fields.\n * Could also be called FieldOptions if passed to the constructor of a\n * Field class.\n *\n * @typedef {Object} FieldDescription\n */\n\n/**\n * A field.\n *\n * TODO: Formalise as a class?\n *\n * @typedef {Object} Field\n *\n * @property {string} path - The path of the field.\n * @property {string} [parent] - The path of the field's parent, if any. Overrides the parent that would otherwise be determined from the `path`.\n * @property {string} [pathFragment] - The leaf of the field's path. TODO: Rename to key\n * @property {string} [type] - The type of the field. Determines the type of value to read and store. Defaults to `'number'`.\n * @property {string} [input] - The input type to use for this field, if any. // TODO: Rename? Might not be an actual input... (i.e. section). `element` might be a good name.\n * @property {Object} [options] - The input options. A free-form object for different input types to interpret and utilise.\n * @property {string} [name] - The field's name. Defaults to a sentence-case translation of the field's key. TODO: Rename to label?\n * @property {string} [description] - The field's description.\n * @property {boolean} [omit=false] - Whether to prevent storing the property's value in data AND prevent updating any children. Defaults to `false`.\n * @property {boolean} [virtual=false] - Whether to prevent storing the property's value in data. Defaults to `false`.\n * @property {string|boolean} [visible=true] - Whether the property is visible. Defaults to `true`. String values are interpreted as expressions.\n * @property {string|boolean} [disabled=false] - Whether the property is disabled. Defaults to `true` if `expression` is set, otherwise defaults to `false`. String values are interpreted as expressions. TODO: Input options?\n * @property {*} [value] - The field's value.\n * @property {*} [default] - The field's default value. Defaults appropriately for the set `type`.\n * @property {boolean} [merge] - Whether to merge the field's non-scalar value with its default value.\n * @property {string} [expression] - An expression used to compute the field's value. Implies `disabled` when set.\n * @property {string} [validator] - The field's validation function. Defaults as appropriate to the `type`.\n * @property {string} [extends] - The path of a field to inherit.\n * @property {string} [extended] - The path of a field that been inherited.\n * @property {string} [mirror] - The path of a field to mirror. TODO: Implement\n * @property {Field[]} [children] - Child fields.\n * @property {string} [template] - Template field that all child fields should extend. Can be a `Field` or a `path` to a field.\n * @property {Object|array} [fixed] - A map or list of child keys that cannot be removed at runtime, if present.\n */\n\n\n\n\n//# sourceURL=webpack://%5Bname%5D/./src/services/FormProcessor.js?"); /***/ }), diff --git a/build/pragma.js b/build/pragma.js index fabec8a..b77fdf2 100644 --- a/build/pragma.js +++ b/build/pragma.js @@ -2887,7 +2887,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var riot /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return FormProcessor; });\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! expr-eval */ \"./node_modules/expr-eval/dist/bundle.js\");\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(expr_eval__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! lodash/clone */ \"./node_modules/lodash/clone.js\");\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lodash_clone__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! lodash/merge */ \"./node_modules/lodash/merge.js\");\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! lodash/defaultsDeep */ \"./node_modules/lodash/defaultsDeep.js\");\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! lodash/map */ \"./node_modules/lodash/map.js\");\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(lodash_map__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! lodash/reduce */ \"./node_modules/lodash/reduce.js\");\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(lodash_reduce__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! lodash/has */ \"./node_modules/lodash/has.js\");\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(lodash_has__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/get */ \"./node_modules/lodash/get.js\");\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_get__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! lodash/set */ \"./node_modules/lodash/set.js\");\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(lodash_set__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! lodash/pull */ \"./node_modules/lodash/pull.js\");\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(lodash_pull__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! lodash/defaultTo */ \"./node_modules/lodash/defaultTo.js\");\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! lodash/difference */ \"./node_modules/lodash/difference.js\");\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(lodash_difference__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! lodash/intersection */ \"./node_modules/lodash/intersection.js\");\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(lodash_intersection__WEBPACK_IMPORTED_MODULE_12__);\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lodash/isPlainObject */ \"./node_modules/lodash/isPlainObject.js\");\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__);\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! lodash/toNumber */ \"./node_modules/lodash/toNumber.js\");\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__);\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! lodash/zipObject */ \"./node_modules/lodash/zipObject.js\");\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__);\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! lodash/sortBy */ \"./node_modules/lodash/sortBy.js\");\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__);\n/* harmony import */ var _mixins_util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../mixins/util */ \"./src/mixins/util.js\");\n/* harmony import */ var _functions_sum__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../functions/sum */ \"./src/functions/sum.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! lodash/sumBy */ \"./node_modules/lodash/sumBy.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default = /*#__PURE__*/__webpack_require__.n(lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__);\n/* harmony import */ var _functions_multiply__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../functions/multiply */ \"./src/functions/multiply.js\");\n/* harmony import */ var _functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../functions/buildDictionary */ \"./src/functions/buildDictionary.js\");\n/* harmony import */ var _functions_buildTree__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../functions/buildTree */ \"./src/functions/buildTree.js\");\n/* harmony import */ var _functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../functions/traverseTree */ \"./src/functions/traverseTree.js\");\n/* harmony import */ var _functions_splitPath__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../functions/splitPath */ \"./src/functions/splitPath.js\");\n/* harmony import */ var _functions_joinPath__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../functions/joinPath */ \"./src/functions/joinPath.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! flat */ \"./node_modules/flat/index.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(flat__WEBPACK_IMPORTED_MODULE_26__);\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! deep-object-diff */ \"./node_modules/deep-object-diff/dist/index.js\");\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27___default = /*#__PURE__*/__webpack_require__.n(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); }\n\nfunction _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar UP = -1;\nvar BOTH = 0;\nvar DOWN = 1;\n/**\n * Pragma form.\n *\n * Expands field lists a dictionary and tree. Processes field expressions from\n * state data.\n *\n * TODO: Rename to Form?\n *\n * @class FormProcessor\n */\n\nvar FormProcessor =\n/*#__PURE__*/\nfunction () {\n /**\n * Create a new property processor.\n *\n * @constructor\n * @param {Field[]} [fields=[]] - Initial form fields.\n * @param {Object.} [functions={}] - Functions to make available for field expressions.\n * @param {Object.} [inputOptions={}] - Default input options keyed by input type.\n */\n function FormProcessor() {\n var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var functions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var inputOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n _classCallCheck(this, FormProcessor);\n\n /**\n * Typecasting functions for each field type.\n *\n * TODO: Strong casting functions\n *\n * @type {Object.}\n */\n this.casts = {\n 'string': function string(f, v) {\n return v == null ? '' : '' + v;\n },\n 'number': function number(f, v) {\n return lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default()(_mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].clamp(v, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.min'), lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.max')));\n },\n 'boolean': function boolean(f, v) {\n return !!v;\n }\n };\n /**\n * Default property values for each field type.\n *\n * @type {Object}\n */\n\n this.defaults = {};\n this.setDefaults({\n '*': {\n type: 'number',\n visible: true\n },\n 'virtual': {\n visible: false,\n virtual: true,\n omit: true\n },\n 'string': {\n input: 'string',\n default: ''\n },\n 'number': {\n input: 'number',\n default: 0,\n options: {\n min: -100,\n max: 100,\n step: 1\n }\n },\n 'boolean': {\n input: 'boolean',\n default: false\n },\n 'selection': {\n input: 'selection',\n options: {\n options: {}\n }\n },\n 'section': {\n input: 'section'\n },\n 'group': {\n input: 'group'\n },\n 'list': {\n input: 'list'\n },\n 'list-item': {\n input: 'list-item'\n },\n 'table': {\n input: 'pragma-table'\n }\n });\n /**\n * Default input options for each input type.\n *\n * @type {Object.}\n */\n\n this.inputOptions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, inputOptions);\n /**\n * Expression functions.\n *\n * @type {Object.}\n */\n\n this.functions = {};\n /**\n * Expression parser.\n *\n * @type {Parser}\n */\n\n this.parser = new expr_eval__WEBPACK_IMPORTED_MODULE_0___default.a.Parser({\n operators: {\n in: true\n }\n }); // Set the functions\n\n this.addFunctions(lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({\n concat: function concat() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return args.join('');\n },\n keys: Object.keys,\n multiply: _functions_multiply__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n sum: _functions_sum__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n sumBy: lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default.a,\n map: lodash_map__WEBPACK_IMPORTED_MODULE_4___default.a,\n reduce: lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default.a\n }, functions));\n /**\n * The set of form fields.\n *\n * @type {Field[]}\n */\n\n this.fields = [];\n /**\n * Fields keyed by path.\n *\n * @type {FieldDictionary}\n */\n\n this.dictionary = {};\n /**\n * The root node of the field tree.\n *\n * @type {Field}\n */\n\n this.tree = {}; // Set the form fields\n\n this.setFields(fields);\n /**\n * Value cache for each field.\n *\n * @type {Object.}\n */\n\n this.valueCache = {};\n /**\n * Field expression cache keyed by path.\n *\n * @type {Object.}\n */\n\n this.expressionCache = {};\n /**\n * Field update dependencies keyed by path.\n *\n * @type {Object.}\n */\n\n this.fieldDependencies = {};\n }\n /**\n * Check whether a field exists at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to check.\n * @return {boolean}\n */\n\n\n _createClass(FormProcessor, [{\n key: \"hasField\",\n value: function hasField(path) {\n return lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(this.dictionary, path);\n }\n /**\n * Get the field at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to get.\n * @return {Field}\n */\n\n }, {\n key: \"getField\",\n value: function getField(path) {\n return this.dictionary[path];\n }\n /**\n * Get the parent field of the field at the given path.\n *\n * @protected\n * @param {Field} field\n * @return {Field|null}\n */\n\n }, {\n key: \"getFieldParent\",\n value: function getFieldParent(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.parent);\n }\n /**\n * Get the ancestors of a field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]}\n */\n\n }, {\n key: \"getFieldAncestors\",\n value: function getFieldAncestors(field) {\n var ancestors = [];\n\n while (field.hasOwnProperty('parent') && this.hasField(field.parent)) {\n field = this.getFieldParent(field);\n ancestors.push(field);\n }\n\n return ancestors;\n }\n /**\n * Get the current value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the value of.\n * @param {*} [data={}] - Optional data to read current values from.\n * @param {*} [value] - Optional current value.\n * @return {*} The current value of the field.\n */\n\n }, {\n key: \"getFieldValue\",\n value: function getFieldValue(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n\n if (!field) {\n return value;\n }\n\n value = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path)); // Merge default values if specified\n\n if (field.merge) {\n if (lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default()(field.default)) {\n return lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, field.default, field.value, value);\n } // TODO: Handle arrays\n\n } // Otherwise use the first defined value\n\n\n return lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n }\n /**\n * Get the default value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the default value of.\n * @return {*} The default value of the field.\n */\n\n }, {\n key: \"getFieldDefaultValue\",\n value: function getFieldDefaultValue(field) {\n if (!field) {\n return null;\n }\n\n return field.default;\n }\n /**\n * Get the template field that a field should a extend.\n *\n * @param {Field} field - The field to get the template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldTemplate\",\n value: function getFieldTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.extends);\n }\n /**\n * Get the keys of the field's children.\n *\n * @protected\n * @param {Field} field - The field to get the child keys of.\n * @return {array} The keys of the field's children.\n */\n\n }, {\n key: \"getFieldChildrenKeys\",\n value: function getFieldChildrenKeys(field) {\n var i,\n keys = [],\n children = field.children || [];\n\n for (i = 0; i < children.length; i++) {\n keys.push(children[i].pathFragment);\n }\n\n return keys;\n }\n /**\n * Get the template that a field's children should extend.\n *\n * @protected\n * @param {Field} field - The field to get the child template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldChildrenTemplate\",\n value: function getFieldChildrenTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.template);\n }\n /**\n * Get the fields dependent upon the given field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]} The dependent fields\n */\n\n }, {\n key: \"getFieldDependencies\",\n value: function getFieldDependencies(field) {\n var dependencies = this.fieldDependencies[field.path]; // Skip if the field has no dependencies\n\n if (!dependencies || !dependencies.length) {\n return [];\n }\n\n var fields = [];\n\n for (var i = 0; i < dependencies.length; i++) {\n var dependency = this.getField(dependencies[i]);\n\n if (!dependency) {\n continue;\n }\n\n fields.push(dependency);\n }\n\n return fields;\n }\n /**\n * Get the current value of a field.\n *\n * Falls back to default values as appropriate.\n *\n * @public\n * @param {string} path - The path to the field.\n * @return {*} The value of the field\n */\n\n }, {\n key: \"getValue\",\n value: function getValue(path) {\n return this.getFieldValue(this.getField(path));\n }\n /**\n * Cast a value based on the property it belongs to.\n *\n * @public\n * @param {Field} field\n * @param {*} value\n */\n\n }, {\n key: \"castValue\",\n value: function castValue(field, value) {\n if (!field) return value;\n if (!this.casts[field.type]) return value; // if (Array.isArray(value))\n // \treturn value.map(this.casts[field.type]);\n\n value = this.casts[field.type](field, value);\n return value;\n }\n /**\n * Derive a field's value from some data.\n *\n * @protected\n * @param {string} path - The path of the field to derive a value for.\n * @param {Object} data - The data to derive values from.\n * @return {*} The derived value.\n */\n\n }, {\n key: \"deriveValue\",\n value: function deriveValue(path, data) {\n // Return from the value cache if a value is set\n if (this.valueCache.hasOwnProperty(path)) {\n return this.valueCache[path];\n }\n\n var field = this.getField(path);\n var value = this.getFieldValue(field, data); // Return the raw value if there's no such field\n\n if (!field) {\n return value;\n } // Cast the value\n\n\n value = this.castValue(field, value); // Evaluate the field's expression\n\n value = this.evaluateFieldExpression(field, data, value); // Fall back to defaults\n //value = defaultTo(value, defaultTo(field.default, null));\n\n value = this.getFieldValue(field, data, value); // Update the value cache\n\n this.valueCache[path] = value;\n return value;\n }\n /**\n * Build a field's expression.\n *\n * @param {Field} field - The field to build an expression for.\n * @return {Expression} The built expression.\n */\n\n }, {\n key: \"buildFieldExpression\",\n value: function buildFieldExpression(field) {\n if (field.expression == null || typeof field.expression !== 'string') {\n return null;\n } // Use the cached expression if one is available\n\n\n if (this.expressionCache[field.path]) {\n return this.expressionCache[field.path];\n } // Build the initial expression\n\n\n var expression;\n\n try {\n expression = this.parser.parse(field.expression);\n } catch (error) {\n console.error(\"Error parsing expression for field '\".concat(field.path, \"': \").concat(error.message));\n return null;\n } // Substitute contextual variables\n\n\n var substitutions = {\n $parent: field.parent\n };\n\n for (var s in substitutions) {\n try {\n expression = expression.substitute(s, substitutions[s]);\n } catch (error) {\n console.error(\"Error substituting expression variable '\".concat(s, \"' for field '\").concat(field.path, \": \").concat(error.message));\n return null;\n }\n }\n\n this.expressionCache[field.path] = expression;\n return expression;\n }\n /**\n * Evaluate a field's value from its expression.\n *\n * Causes the evaluation of any field dependencies as a result.\n *\n * TODO: Evaluate (and cache) expressions for other field properties! :D\n *\n * @param {Field} field - The field to compute the value of.\n * @param {Object} data - The data to derive values from.\n * @param {*} [value] - The current value of the field.\n * @return {*} The computed value of the field's expression.\n */\n\n }, {\n key: \"evaluateFieldExpression\",\n value: function evaluateFieldExpression(field, data, value) {\n var _this = this;\n\n value = this.getFieldValue(field, data, value); // Parse the expression\n\n var expression = this.buildFieldExpression(field);\n\n if (!expression) {\n return value;\n } // TODO: Extract deriving variables and building contextual functions\n // let variables = buildExpressionContext(field, data, expression, value)?\n // Contextual functions should still update the same \"variables\" reference\n // Derive values for the variables in the expression\n\n\n var variables = expression.variables({\n withMembers: true\n });\n var values = {\n $this: field,\n $value: value\n };\n\n for (var v = 0; v < variables.length; v++) {\n var variable = variables[v];\n if (lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(values, variable)) continue;\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(values, variable, this.deriveValue(variable, data));\n } // Build contextual functions\n\n\n values = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(values, {\n field: function field(path) {\n variables.push(path);\n return _this.getField(path);\n },\n value: function value(path) {\n // Add the path to the list of expression variables\n variables.push(path); // Derive the value for the expression\n\n return _this.deriveValue(path, data);\n }\n }); // Evaluate the expression\n\n try {\n value = expression.evaluate(values);\n } catch (error) {\n console.log('evaluateFieldExpression', field, data, value);\n console.error(\"Error evaluating expression for field '\".concat(field.path, \"': \").concat(error.message));\n } //console.log('evaluateFieldExpression', field.path, expression.toString(), variables, values, value);\n //console.log('evaluateFieldExpression expression', expression);\n // Update the map of field update dependencies\n // TODO: Exclude contextual variables\n // TODO: Move this to an earlier processing step that evaluates the\n // expression with spy functions\n\n\n for (var _v = 0; _v < variables.length; _v++) {\n var _variable = variables[_v];\n this.fieldDependencies[_variable] = this.fieldDependencies[_variable] || [];\n\n if (this.fieldDependencies[_variable].indexOf(field.path) < 0) {\n this.fieldDependencies[_variable].push(field.path);\n }\n }\n\n return value;\n }\n /**\n * Prepare fields from a set of field descriptions.\n *\n * Ascertain's the parent path and path fragment (key) of each field.\n *\n * TODO: Field class, FieldDescription typedef.\n *\n * @protected\n * @param {Field[]} fields - The field description.\n * @returns {Field[]} The given fields prepared with pathFragment and parent properties.\n */\n\n }, {\n key: \"prepareFields\",\n value: function prepareFields(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field, pathFragment, parentPath;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Ascertain a parent path and path fragment\n\n var _splitPath = Object(_functions_splitPath__WEBPACK_IMPORTED_MODULE_24__[\"default\"])(field.path);\n\n var _splitPath2 = _slicedToArray(_splitPath, 2);\n\n parentPath = _splitPath2[0];\n pathFragment = _splitPath2[1];\n field.pathFragment = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.pathFragment, pathFragment);\n field.parent = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.parent, parentPath);\n }\n\n return fields;\n }\n /**\n * Set the form's fields.\n *\n * TODO: Creates, updates and removes fields accordingly.\n *\n * @public\n * @param {Field[]} fields\n */\n\n }, {\n key: \"setFields\",\n value: function setFields(fields) {\n // Prepare the fields\n this.fields = this.prepareFields(fields); // TODO: Build dictionary from given fields, compare with current fields\n //this.dictionary = this.buildDictionary(this.fields);\n\n this.dictionary = this.updateDictionary(this.fields); // Compose the fields into a tree\n\n this.tree = this.buildTree(this.dictionary); // Clear all caches\n\n this.valueCache = {};\n this.expressionCache = {};\n this.fieldDependencies = {};\n }\n /**\n * Add a form field.\n *\n * @param {Field} field\n */\n\n }, {\n key: \"addField\",\n value: function addField(field) {\n var parent = this.getFieldParent(field);\n\n if (!parent) {\n console.warn(\"Could not set field \".concat(field.path, \" - its parent does not exist\"));\n return;\n }\n\n if (!parent.children) {\n parent.children = [];\n }\n\n if (!parent.children.includes(field)) {\n parent.children.push(field);\n }\n\n this.dictionary[field.path] = field;\n }\n /**\n * Update a property with the given value.\n *\n * @public\n * @param {Object} data - The data to update.\n * @param {string} path - The path of the field to update.\n * @param {*} value - The value to set.\n * @return {*} The updated value\n */\n\n }, {\n key: \"setValue\",\n value: function setValue(data, path, value) {\n var field = this.getField(path); // Update the value if one is given\n\n if (value !== undefined) {\n if (field) {\n field.value = value;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, value);\n } // Update the field at this path\n\n\n this.updatePath(path, data); // Get the updated value\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path);\n }\n /**\n * Clear the value cache.\n *\n * Optionally accepts a path to clear.\n *\n * @param {string} path\n */\n\n }, {\n key: \"clearValueCache\",\n value: function clearValueCache() {\n var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n\n // Just empty the entire cache if there's no path is or there's no field\n // at the given path\n if (!path || !this.getField(path)) {\n this.valueCache = {};\n return;\n }\n\n var i,\n field = this.getField(path); // Clear cached values of child fields iteratively\n\n var child,\n children = field.children,\n nextChildren = [];\n\n while (children && children.length) {\n nextChildren = [];\n\n for (i = 0; i < children.length; i++) {\n child = children[i];\n delete this.valueCache[child.path];\n\n if (child.children) {\n nextChildren = nextChildren.concat(child.children);\n }\n }\n\n children = nextChildren;\n } // Clear the cached value for this field\n\n\n delete this.valueCache[field.path]; // Clear cached values of parent fields iteratively\n\n var ancestors = this.getFieldAncestors(field);\n\n for (i = 0; i < ancestors.length; i++) {\n delete this.valueCache[ancestors[i].path];\n }\n }\n /**\n * Derive a property's name from its path.\n *\n * @protected\n * @param {Field} field\n * @return {string} The derived name\n */\n\n }, {\n key: \"deriveName\",\n value: function deriveName(field) {\n var path = field.path;\n var lastDotIndex = path.lastIndexOf('.');\n return _mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].sentenceCase(path.substring(lastDotIndex + 1));\n }\n /**\n * Set the default field properties for each type.\n *\n * @param {Object.} fieldProperties - The default field properties, keyed by field type.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(fieldProperties) {\n this.defaults = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.defaults, fieldProperties);\n }\n /**\n * Apply the form's default properties to the given fields.\n *\n * Fills in default values, derives default names.\n *\n * @protected\n * @param {Field[]} fields - The fields to apply default values to.\n * @returns {Field[]}\n */\n\n }, {\n key: \"applyDefaults\",\n value: function applyDefaults(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Derive a name\n\n if (field.name === undefined) {\n field.name = this.deriveName(field);\n } // Apply global defaults\n\n\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults['*']); // Apply type-specific defaults\n\n if (this.defaults[field.type]) {\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults[field.type]);\n } // Apply default input options\n\n\n if (this.inputOptions[field.input]) {\n field.options = field.options || {};\n field.options = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field.options, this.inputOptions[field.input]);\n } // Disable the field implicitly if it has an expression\n\n\n if (!field.hasOwnProperty('disabled')) {\n field.disabled = !!field.expression;\n }\n }\n\n return fields;\n }\n /**\n * Add to the form's functions.\n *\n * @param {Object.} functions - Functions to add, keyed by name.\n */\n\n }, {\n key: \"addFunctions\",\n value: function addFunctions(functions) {\n // Add the functions to the form\n this.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.functions, functions); // Add the functions to the expression parser\n\n this.parser.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.parser.functions, this.functions); // Clear the expression cache\n\n this.expressionCache = {};\n }\n /**\n * Update the form using the given data.\n *\n * @public\n * @param {Object} [data] - The data to update with.\n */\n\n }, {\n key: \"update\",\n value: function update(data) {\n // Update the value of every field\n this.updatePath('', data); // TODO: Diff any *paths* that changed and update those\n // i.e. implement diffFieldDataPaths()\n\n console.time('buildData');\n var formData = this.buildData(this.tree);\n console.timeEnd('buildData');\n console.log(formData);\n console.time('diff');\n var diff = Object(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__[\"detailedDiff\"])(formData, data);\n console.timeEnd('diff');\n console.log(diff);\n console.time('flatten');\n var addedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.added);\n var updatedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.updated);\n var deletedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.deleted);\n console.timeEnd('flatten');\n console.log(addedPaths, updatedPaths, deletedPaths); //let path;\n // for (path in addedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // for (path in updatedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n //\n // for (path in deletedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n\n console.time('traverseTree');\n Object(_functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__[\"default\"])(this.tree, function (field) {// Pre-order\n }, function (field) {\n // Post-order\n console.log(field.path);\n });\n console.timeEnd('traverseTree');\n }\n /**\n * Update the field at the given path.\n *\n * @param {string} path - The path of the field to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updatePath\",\n value: function updatePath(path, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n this.updateField(this.getField(path), data, direction);\n }\n /**\n * Update the given fields with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field[]} fields - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateFields\",\n value: function updateFields(fields, data, direction) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.updateField(fields[i], data, direction);\n }\n }\n /**\n * Update the given field with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field} field - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateField\",\n value: function updateField(field, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n\n if (!field) {\n return;\n } //console.log('updateField()', field.path);\n\n\n console.count('updateField()');\n this.clearValueCache(field.path); // Update the field's children\n\n if (direction >= 0) {\n // TODO: This would be a good spot for an event to fire to allow plugins\n // (like inheritance) to intercept child update behaviour\n this.updateFieldInheritance(field, data);\n\n if (!field.omit) {\n this.updateFields(field.children, data);\n }\n } // Apply default values\n\n\n this.applyDefaults([field]); // Update the state's value\n\n this.updateDataValue(field, data); // Update the field's value (and update all fields dependent on this one)\n\n this.updateFieldValue(field, data, direction <= 0);\n }\n /**\n * Update data from the given field.\n *\n * @protected\n * @param {Field} field - The field to update with.\n * @param {Object} data - The data to update.\n * @return {Object} The updated data.\n */\n\n }, {\n key: \"updateDataValue\",\n value: function updateDataValue(field, data) {\n if (!field || field.omit || field.virtual) {\n return data;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, this.deriveValue(field.path, data));\n return data;\n }\n /**\n * Update a field using the given data.\n *\n * @protected\n * @param {Field} field - The field to update.\n * @param {Object} data - The data to update with.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldValue\",\n value: function updateFieldValue(field, data) {\n var updateParents = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!field) {\n return;\n } // Update the field value\n\n\n field.value = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path); // Update all fields dependent on this one\n\n this.updateFieldDependencies(field, data, updateParents);\n }\n /**\n * Update fields that are dependent upon the value of the given field.\n *\n * @protected\n * @param {Field} field - The field to update dependencies of.\n * @param {Object} data - The data to update from.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldDependencies\",\n value: function updateFieldDependencies(field, data, updateParents) {\n // Recursively update parent field values\n if (updateParents) {\n this.updateFieldAncestors(field, data);\n } // Update fields listed as dependencies\n\n\n this.updateFields(this.getFieldDependencies(field), data);\n }\n /**\n * Update the ancestors of the given field.\n *\n * @protected\n * @param {Field} field - The field to update parents of.\n * @param {Object} data - The data to update with.\n */\n\n }, {\n key: \"updateFieldAncestors\",\n value: function updateFieldAncestors(field, data) {\n var i,\n parents = this.getFieldAncestors(field);\n\n for (i = 0; i < parents.length; i++) {\n this.updateFieldValue(parents[i], data);\n }\n }\n /**\n * Update a field's inheritance.\n *\n * Ensures that inherited child fields exist.\n *\n * @param {Field} field - The field to update the inheritance of.\n * @param {Object} data - The date to update with.\n */\n\n }, {\n key: \"updateFieldInheritance\",\n value: function updateFieldInheritance(field, data) {\n // Update child template fields\n this.updateTemplateFields(field, data); // Inherit the field's template\n\n this.inheritTemplate(field, data);\n }\n /**\n * Get the keys of child fields that don't exist in the given data.\n *\n * Retrieves the new keys, existing keys and old keys of a field compared\n * to its data.\n *\n * @protected\n * @param {Field} field - The field with a template.\n * @param {Object} data - The data to diff against.\n * @return array [newPaths[], existingPaths[], oldPaths[]]\n */\n\n }, {\n key: \"diffFieldDataKeys\",\n value: function diffFieldDataKeys(field, data) {\n // Grab the data keys and child field keys\n var childData = this.getFieldValue(field, data);\n var childDataKeys = childData ? Object.keys(childData) : [];\n var childFieldKeys = this.getFieldChildrenKeys(field); // Keys in data that aren't in fields\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childDataKeys, childFieldKeys); // Keys in data and fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(childDataKeys, childFieldKeys); // Keys in fields that aren't in data\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childFieldKeys, childDataKeys); // console.log('diffTemplateFieldKeys()', field.path, 'newKeys', newKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'existingKeys', existingKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'oldKeys', oldKeys);\n\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Unravel all templates into fields for the given field and data.\n *\n * TODO: Try to merge this into inheritTemplate(), or extract a method\n * that can handle both cases, like updateFieldChildrenInheritance().\n *\n * @protected\n * @param {Field} field - The field to update template fields for.\n * @param {Object} [data] - The data used to unravel field templates.\n */\n\n }, {\n key: \"updateTemplateFields\",\n value: function updateTemplateFields(field, data) {\n if (!field) {\n return;\n }\n\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return;\n }\n\n var dictionary = this.dictionary,\n i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Find child fields that need to be added, updated or removed\n\n var _this$diffFieldDataKe = this.diffFieldDataKeys(field, data),\n _this$diffFieldDataKe2 = _slicedToArray(_this$diffFieldDataKe, 3),\n newKeys = _this$diffFieldDataKe2[0],\n existingKeys = _this$diffFieldDataKe2[1],\n oldKeys = _this$diffFieldDataKe2[2];\n\n var existingFieldKeys = this.getFieldChildrenKeys(field);\n var fixedKeys = field.fixed || []; // TODO: Extract to... diffFixedFieldDataKeys()...?\n // Remove fixed keys from the keys that need removing\n\n oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(oldKeys, fixedKeys); // Add the existent fixed keys to the keys that need updating\n\n existingKeys = existingKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(fixedKeys, existingFieldKeys), existingKeys, newKeys)); // Add the non-existent fixed keys to the keys that need creating\n\n newKeys = newKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(fixedKeys, existingFieldKeys, newKeys)); //console.log(field.path, newKeys, existingKeys, oldKeys, fixedKeys, existingFieldKeys);\n // Remove old fields\n\n for (i = 0; i < oldKeys.length; i++) {\n key = oldKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n this.removeField(this.getField(path));\n } // Update existing fields\n\n\n for (i = 0; i < existingKeys.length; i++) {\n key = existingKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() existingField', field.path, path);\n // Ensure the existing field has the correct template\n\n existingField = this.getField(path);\n existingField.extends = template.path;\n } // Build new fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field);\n\n for (i = 0; i < newKeys.length; i++) {\n key = newKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() newField', path, value[key]);\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: template.path\n };\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields);\n }\n\n this.updateDictionary(newFields);\n }\n /**\n * Diff the keys of the first field's children with those of the second\n * field's children.\n *\n * Finds keys of the first that aren't of the second, keys that are of both,\n * and keys of the second that aren't of the first.\n *\n * @protected\n * @param {Field} firstField\n * @param {Field} secondField\n * @returns {array} [newKeys, existingKeys, oldKeys]\n */\n\n }, {\n key: \"diffFieldChildrenKeys\",\n value: function diffFieldChildrenKeys(firstField, secondField) {\n var firstFieldKeys = this.getFieldChildrenKeys(firstField);\n var secondFieldKeys = this.getFieldChildrenKeys(secondField); // Child keys of the first field that aren't of the second field\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(firstFieldKeys, secondFieldKeys); // Keys in the children of both fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(firstFieldKeys, secondFieldKeys); // Child keys of the second field that aren't of the first\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(secondFieldKeys, firstFieldKeys);\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Apply a field's inheritance.\n *\n * Ensures that a field inherits from its base field.\n *\n * @param {Field} field - The inheriting field.\n * @param {Object} data - The data to update with.\n * @return {Field}\n */\n\n }, {\n key: \"inheritTemplate\",\n value: function inheritTemplate(field, data) {\n if (!field) {\n return field;\n }\n\n var template = this.getFieldTemplate(field); // Skip fields without a template\n\n if (!template) {\n return field;\n } // Inherit the template\n\n\n if (field.extended !== template.path) {\n var templateClone = lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(template); // We don't want to inherit children, nor do we support more than a\n // single layer of inheritance\n\n delete templateClone.children;\n delete templateClone.template; // if (field.path === 'skills.list.test.ability') {\n // \tconsole.log('skills.list.test.ability', clone(field), templateClone);\n // }\n // Merge sandwich to retain original values\n\n field = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(field, templateClone, lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(field));\n\n if (field.name == null) {\n field.name = this.deriveName(field);\n }\n\n field.extended = template.path;\n this.prepareFields([field]);\n } //console.log('inheritTemplate()', field.path, template.path);\n // Update child field inheritance\n\n\n var i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Diff field child keys and template child keys\n\n var _this$diffFieldChildr = this.diffFieldChildrenKeys(template, field),\n _this$diffFieldChildr2 = _slicedToArray(_this$diffFieldChildr, 2),\n newKeys = _this$diffFieldChildr2[0],\n existingKeys = _this$diffFieldChildr2[1]; // Update existing template fields\n\n\n for (var _i2 = 0; _i2 < existingKeys.length; _i2++) {\n key = existingKeys[_i2];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n existingField = this.getField(path); //console.log('inheritTemplate() existingField', path, existingField);\n\n existingField.extends = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key);\n } // Build new template fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field); //console.log('inheritTemplate()', field.path, value);\n\n for (var _i3 = 0; _i3 < newKeys.length; _i3++) {\n key = newKeys[_i3];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('inherit newKey', path, value[key]);\n // Build the new field\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key)\n }; // Propagate values\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n // TODO: Extract addFieldChildren(field, children)\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields); // We then want to sort these in the order of template's children\n // TODO: Extract sortFieldChildren(field)\n\n var templateChildKeys = this.getFieldChildrenKeys(template); // We flip the child keys into an object where the values are the\n // indices of the child keys array\n\n templateChildKeys = lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default()(templateChildKeys, _toConsumableArray(templateChildKeys.keys())); // This makes it easier to sort\n\n field.children = lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default()(field.children, function (child) {\n return templateChildKeys[child.pathFragment];\n });\n }\n\n this.updateDictionary(newFields);\n return field;\n }\n /**\n * Remove data from the given path.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path to remove.\n */\n\n }, {\n key: \"removeValue\",\n value: function removeValue(data, path) {\n this.removePath(path, data);\n }\n /**\n * Remove data at the given path and update parent fields.\n *\n * TODO: The naming and existence of this method doesn't quite make sense.\n * You'd expect it to remove the field(s) too.\n * Refactor!\n *\n * @protected\n * @param {string} path - The path to remove.\n * @param {Object} data - The data to remove the path from.\n */\n\n }, {\n key: \"removePath\",\n value: function removePath(path, data) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Remove the state data\n\n\n this.removeData(field, data); // Update the parent field\n\n this.updateField(this.getFieldParent(field), data);\n }\n /**\n * Remove the given fields.\n *\n * @protected\n * @param {Field[]} fields - The fields to remove.\n */\n\n }, {\n key: \"removeFields\",\n value: function removeFields(fields) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.removeField(fields[i]);\n }\n }\n /**\n * Remove a field.\n *\n * Clears dictionary and parent references to the field.\n *\n * Doesn't remove data or update parent field values.\n *\n * @protected\n * @param {Field} field - The field to remove.\n */\n\n }, {\n key: \"removeField\",\n value: function removeField(field) {\n if (!field) {\n return;\n }\n\n this.clearValueCache(field.path); // Remove the field's children\n\n this.removeFields(field.children); // Remove the field from the dictionary and dependency list\n\n delete this.dictionary[field.path];\n delete this.fieldDependencies[field.path]; // Remove the field from its parent\n\n var parent = this.getFieldParent(field);\n lodash_pull__WEBPACK_IMPORTED_MODULE_9___default()(parent.children, field);\n }\n /**\n * Remove a field's path from the given data.\n *\n * @param {Field} field - The field whose path to remove from data.\n * @param {Object} data - The data to remove from.\n */\n\n }, {\n key: \"removeData\",\n value: function removeData(field, data) {\n if (!field) {\n return;\n }\n\n var parent = this.getField(field.parent);\n\n if (!parent) {\n return;\n }\n\n var parentPath = parent.path;\n var parentValue = this.getFieldValue(parent, data);\n var key = field.pathFragment;\n\n if (Array.isArray(parentValue)) {\n parentValue.splice(key, 1);\n } else {\n delete parentValue[key];\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, parentPath, parentValue);\n }\n /**\n * Update the dictionary with the given fields.\n *\n * @param {Field[]} fields\n * @returns {FieldDictionary}\n */\n\n }, {\n key: \"updateDictionary\",\n value: function updateDictionary(fields) {\n if (!Array.isArray(fields)) {\n return this.dictionary;\n }\n\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i] || !fields[i].path) {\n continue;\n }\n\n this.dictionary[fields[i].path] = fields[i];\n }\n\n return this.dictionary;\n }\n /**\n * Build a dictionary from the given fields.\n *\n * @param {Field[]} fields\n * @return {FieldDictionary}\n */\n\n }, {\n key: \"buildDictionary\",\n value: function buildDictionary(fields) {\n return Object(_functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__[\"default\"])(fields);\n }\n /**\n * Build a tree from the given dictionary.\n *\n * @protected\n * @param {FieldDictionary} dictionary\n * @returns {Field}\n */\n\n }, {\n key: \"buildTree\",\n value: function buildTree(dictionary) {\n return Object(_functions_buildTree__WEBPACK_IMPORTED_MODULE_22__[\"default\"])(dictionary);\n }\n /**\n * Build data from the current field state.\n *\n * @param {Field} field - The root field to traverse from.\n * @param {Object} [data={}] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildData\",\n value: function buildData(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (!field) {\n return data;\n }\n\n var child, childData; // If the field has children, build the data of its children\n\n if (field.children) {\n for (var c = 0; c < field.children.length; c++) {\n child = field.children[c];\n\n if (child.virtual || child.omit) {\n continue;\n }\n\n childData = this.buildData(field.children[c], data);\n }\n\n return data;\n } // Set data\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n return data;\n }\n /**\n * Build data for a field's template.\n *\n * @protected\n * @param {Field} field - The field to build child data for.\n * @param {Object} [data] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildTemplateData\",\n value: function buildTemplateData(field, data) {\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return null;\n }\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(this.buildData(template), template.path);\n }\n /**\n * Add new child data for the field at the given path using its template.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path of the field to add new child data to.\n * @param {string|number} [key] - Optional key to use for the new child data.\n */\n\n }, {\n key: \"addItem\",\n value: function addItem(data, path, key) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Build the new child data\n\n\n var newData = this.buildTemplateData(field); // Get the target for the data\n\n var target = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path, []); // Add the new child data to the collection\n\n if (Array.isArray(target)) {\n target.push(newData);\n } else if (key != null && _typeof(target) === 'object') {\n target[key] = newData;\n } else {\n // Bail if we're not dealing with a collection\n console.warn(\"Could not create new child data for '\".concat(path, \"';\") + \" either it wasn't an array or wasn't an object with a key provided\");\n return;\n } // Set it back\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, target); // Update the form\n\n this.updatePath(path, data);\n }\n }]);\n\n return FormProcessor;\n}();\n/**\n * A dictionary of fields.\n *\n * Fields are keyed by their path.\n *\n * @typedef {Object.} FieldDictionary\n */\n\n/**\n * A field description.\n *\n * TODO: Update this to reflect the simplest approach to describing fields.\n * Could also be called FieldOptions if passed to the constructor of a\n * Field class.\n *\n * @typedef {Object} FieldDescription\n */\n\n/**\n * A field.\n *\n * TODO: Formalise as a class?\n *\n * @typedef {Object} Field\n *\n * @property {string} path - The path of the field.\n * @property {string} [parent] - The path of the field's parent, if any. Overrides the parent that would otherwise be determined from the `path`.\n * @property {string} [pathFragment] - The leaf of the field's path. TODO: Rename to key\n * @property {string} [type] - The type of the field. Determines the type of value to read and store. Defaults to `'number'`.\n * @property {string} [input] - The input type to use for this field, if any. // TODO: Rename? Might not be an actual input... (i.e. section). `element` might be a good name.\n * @property {Object} [options] - The input options. A free-form object for different input types to interpret and utilise.\n * @property {string} [name] - The field's name. Defaults to a sentence-case translation of the field's key. TODO: Rename to label?\n * @property {string} [description] - The field's description.\n * @property {boolean} [omit=false] - Whether to prevent storing the property's value in data AND prevent updating any children. Defaults to `false`.\n * @property {boolean} [virtual=false] - Whether to prevent storing the property's value in data. Defaults to `false`.\n * @property {string|boolean} [visible=true] - Whether the property is visible. Defaults to `true`. String values are interpreted as expressions.\n * @property {string|boolean} [disabled=false] - Whether the property is disabled. Defaults to `true` if `expression` is set, otherwise defaults to `false`. String values are interpreted as expressions. TODO: Input options?\n * @property {*} [value] - The field's value.\n * @property {*} [default] - The field's default value. Defaults appropriately for the set `type`.\n * @property {boolean} [merge] - Whether to merge the field's non-scalar value with its default value.\n * @property {string} [expression] - An expression used to compute the field's value. Implies `disabled` when set.\n * @property {string} [validator] - The field's validation function. Defaults as appropriate to the `type`.\n * @property {string} [extends] - The path of a field to inherit.\n * @property {string} [extended] - The path of a field that been inherited.\n * @property {string} [mirror] - The path of a field to mirror. TODO: Implement\n * @property {Field[]} [children] - Child fields.\n * @property {string} [template] - Template field that all child fields should extend. Can be a `Field` or a `path` to a field.\n * @property {Object|array} [fixed] - A map or list of child keys that cannot be removed at runtime, if present.\n */\n\n\n\n\n//# sourceURL=webpack://%5Bname%5D/./src/services/FormProcessor.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return FormProcessor; });\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! expr-eval */ \"./node_modules/expr-eval/dist/bundle.js\");\n/* harmony import */ var expr_eval__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(expr_eval__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! lodash/clone */ \"./node_modules/lodash/clone.js\");\n/* harmony import */ var lodash_clone__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(lodash_clone__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! lodash/merge */ \"./node_modules/lodash/merge.js\");\n/* harmony import */ var lodash_merge__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(lodash_merge__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! lodash/defaultsDeep */ \"./node_modules/lodash/defaultsDeep.js\");\n/* harmony import */ var lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! lodash/map */ \"./node_modules/lodash/map.js\");\n/* harmony import */ var lodash_map__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(lodash_map__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! lodash/reduce */ \"./node_modules/lodash/reduce.js\");\n/* harmony import */ var lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(lodash_reduce__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! lodash/has */ \"./node_modules/lodash/has.js\");\n/* harmony import */ var lodash_has__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(lodash_has__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/get */ \"./node_modules/lodash/get.js\");\n/* harmony import */ var lodash_get__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_get__WEBPACK_IMPORTED_MODULE_7__);\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! lodash/set */ \"./node_modules/lodash/set.js\");\n/* harmony import */ var lodash_set__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(lodash_set__WEBPACK_IMPORTED_MODULE_8__);\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! lodash/pull */ \"./node_modules/lodash/pull.js\");\n/* harmony import */ var lodash_pull__WEBPACK_IMPORTED_MODULE_9___default = /*#__PURE__*/__webpack_require__.n(lodash_pull__WEBPACK_IMPORTED_MODULE_9__);\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! lodash/defaultTo */ \"./node_modules/lodash/defaultTo.js\");\n/* harmony import */ var lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default = /*#__PURE__*/__webpack_require__.n(lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10__);\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! lodash/difference */ \"./node_modules/lodash/difference.js\");\n/* harmony import */ var lodash_difference__WEBPACK_IMPORTED_MODULE_11___default = /*#__PURE__*/__webpack_require__.n(lodash_difference__WEBPACK_IMPORTED_MODULE_11__);\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! lodash/intersection */ \"./node_modules/lodash/intersection.js\");\n/* harmony import */ var lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default = /*#__PURE__*/__webpack_require__.n(lodash_intersection__WEBPACK_IMPORTED_MODULE_12__);\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! lodash/isPlainObject */ \"./node_modules/lodash/isPlainObject.js\");\n/* harmony import */ var lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default = /*#__PURE__*/__webpack_require__.n(lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13__);\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! lodash/toNumber */ \"./node_modules/lodash/toNumber.js\");\n/* harmony import */ var lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(lodash_toNumber__WEBPACK_IMPORTED_MODULE_14__);\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! lodash/zipObject */ \"./node_modules/lodash/zipObject.js\");\n/* harmony import */ var lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default = /*#__PURE__*/__webpack_require__.n(lodash_zipObject__WEBPACK_IMPORTED_MODULE_15__);\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! lodash/sortBy */ \"./node_modules/lodash/sortBy.js\");\n/* harmony import */ var lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default = /*#__PURE__*/__webpack_require__.n(lodash_sortBy__WEBPACK_IMPORTED_MODULE_16__);\n/* harmony import */ var _mixins_util__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../mixins/util */ \"./src/mixins/util.js\");\n/* harmony import */ var _functions_sum__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ../functions/sum */ \"./src/functions/sum.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! lodash/sumBy */ \"./node_modules/lodash/sumBy.js\");\n/* harmony import */ var lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default = /*#__PURE__*/__webpack_require__.n(lodash_sumBy__WEBPACK_IMPORTED_MODULE_19__);\n/* harmony import */ var _functions_multiply__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ../functions/multiply */ \"./src/functions/multiply.js\");\n/* harmony import */ var _functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ../functions/buildDictionary */ \"./src/functions/buildDictionary.js\");\n/* harmony import */ var _functions_buildTree__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ../functions/buildTree */ \"./src/functions/buildTree.js\");\n/* harmony import */ var _functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ../functions/traverseTree */ \"./src/functions/traverseTree.js\");\n/* harmony import */ var _functions_splitPath__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ../functions/splitPath */ \"./src/functions/splitPath.js\");\n/* harmony import */ var _functions_joinPath__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ../functions/joinPath */ \"./src/functions/joinPath.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! flat */ \"./node_modules/flat/index.js\");\n/* harmony import */ var flat__WEBPACK_IMPORTED_MODULE_26___default = /*#__PURE__*/__webpack_require__.n(flat__WEBPACK_IMPORTED_MODULE_26__);\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! deep-object-diff */ \"./node_modules/deep-object-diff/dist/index.js\");\n/* harmony import */ var deep_object_diff__WEBPACK_IMPORTED_MODULE_27___default = /*#__PURE__*/__webpack_require__.n(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }\n\nfunction _nonIterableSpread() { throw new TypeError(\"Invalid attempt to spread non-iterable instance\"); }\n\nfunction _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter); }\n\nfunction _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }\n\nfunction _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }\n\nfunction _nonIterableRest() { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); }\n\nfunction _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"] != null) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; }\n\nfunction _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar UP = -1;\nvar BOTH = 0;\nvar DOWN = 1;\n/**\n * Pragma form.\n *\n * Expands field lists a dictionary and tree. Processes field expressions from\n * state data.\n *\n * TODO: Rename to Form?\n *\n * @class FormProcessor\n */\n\nvar FormProcessor =\n/*#__PURE__*/\nfunction () {\n /**\n * Create a new property processor.\n *\n * @constructor\n * @param {Field[]} [fields=[]] - Initial form fields.\n * @param {Object.} [functions={}] - Functions to make available for field expressions.\n * @param {Object.} [inputOptions={}] - Default input options keyed by input type.\n */\n function FormProcessor() {\n var fields = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var functions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var inputOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n _classCallCheck(this, FormProcessor);\n\n /**\n * Typecasting functions for each field type.\n *\n * TODO: Strong casting functions\n *\n * @type {Object.}\n */\n this.casts = {\n 'string': function string(f, v) {\n return v == null ? '' : '' + v;\n },\n 'number': function number(f, v) {\n return lodash_toNumber__WEBPACK_IMPORTED_MODULE_14___default()(_mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].clamp(v, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.min'), lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(f, 'options.max')));\n },\n 'boolean': function boolean(f, v) {\n return !!v;\n }\n };\n /**\n * Default property values for each field type.\n *\n * @type {Object}\n */\n\n this.defaults = {};\n this.setDefaults({\n '*': {\n type: 'number',\n visible: true\n },\n 'virtual': {\n visible: false,\n virtual: true,\n omit: true\n },\n 'string': {\n input: 'string',\n default: ''\n },\n 'number': {\n input: 'number',\n default: 0,\n options: {\n min: -100,\n max: 100,\n step: 1\n }\n },\n 'boolean': {\n input: 'boolean',\n default: false\n },\n 'selection': {\n input: 'selection',\n options: {\n options: {}\n }\n },\n 'section': {\n input: 'section'\n },\n 'group': {\n input: 'group'\n },\n 'list': {\n input: 'list'\n },\n 'list-item': {\n input: 'list-item'\n },\n 'table': {\n input: 'pragma-table'\n }\n });\n /**\n * Default input options for each input type.\n *\n * @type {Object.}\n */\n\n this.inputOptions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, inputOptions);\n /**\n * Expression functions.\n *\n * @type {Object.}\n */\n\n this.functions = {};\n /**\n * Expression parser.\n *\n * @type {Parser}\n */\n\n this.parser = new expr_eval__WEBPACK_IMPORTED_MODULE_0___default.a.Parser({\n operators: {\n in: true\n }\n }); // Set the functions\n\n this.addFunctions(lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({\n concat: function concat() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return args.join('');\n },\n keys: Object.keys,\n multiply: _functions_multiply__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n sum: _functions_sum__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n sumBy: lodash_sumBy__WEBPACK_IMPORTED_MODULE_19___default.a,\n map: lodash_map__WEBPACK_IMPORTED_MODULE_4___default.a,\n reduce: lodash_reduce__WEBPACK_IMPORTED_MODULE_5___default.a\n }, functions));\n /**\n * The set of form fields.\n *\n * @type {Field[]}\n */\n\n this.fields = [];\n /**\n * Fields keyed by path.\n *\n * @type {FieldDictionary}\n */\n\n this.dictionary = {};\n /**\n * The root node of the field tree.\n *\n * @type {Field}\n */\n\n this.tree = {}; // Set the form fields\n\n this.setFields(fields);\n /**\n * Value cache for each field.\n *\n * @type {Object.}\n */\n\n this.valueCache = {};\n /**\n * Field expression cache keyed by path.\n *\n * @type {Object.}\n */\n\n this.expressionCache = {};\n /**\n * Field update dependencies keyed by path.\n *\n * @type {Object.}\n */\n\n this.fieldDependencies = {};\n /**\n * Map of updated fields.\n *\n * Used to prevent updating fields more than once.\n *\n * @type {Object.}\n */\n\n this.updatedFields = {};\n }\n /**\n * Check whether a field exists at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to check.\n * @return {boolean}\n */\n\n\n _createClass(FormProcessor, [{\n key: \"hasField\",\n value: function hasField(path) {\n return lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(this.dictionary, path);\n }\n /**\n * Get the field at the given path.\n *\n * @protected\n * @param {string} path - The path of the field to get.\n * @return {Field}\n */\n\n }, {\n key: \"getField\",\n value: function getField(path) {\n return this.dictionary[path];\n }\n /**\n * Get the parent field of the field at the given path.\n *\n * @protected\n * @param {Field} field\n * @return {Field|null}\n */\n\n }, {\n key: \"getFieldParent\",\n value: function getFieldParent(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.parent);\n }\n /**\n * Get the ancestors of a field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]}\n */\n\n }, {\n key: \"getFieldAncestors\",\n value: function getFieldAncestors(field) {\n var ancestors = [];\n\n while (field.hasOwnProperty('parent') && this.hasField(field.parent)) {\n field = this.getFieldParent(field);\n ancestors.push(field);\n }\n\n return ancestors;\n }\n /**\n * Get the current value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the value of.\n * @param {*} [data={}] - Optional data to read current values from.\n * @param {*} [value] - Optional current value.\n * @return {*} The current value of the field.\n */\n\n }, {\n key: \"getFieldValue\",\n value: function getFieldValue(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n\n if (!field) {\n return value;\n }\n\n value = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path)); // Merge default values if specified\n\n if (field.merge) {\n if (lodash_isPlainObject__WEBPACK_IMPORTED_MODULE_13___default()(field.default)) {\n return lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()({}, field.default, field.value, value);\n } // TODO: Handle arrays\n\n } // Otherwise use the first defined value\n\n\n return lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(value, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n }\n /**\n * Get the default value of a field.\n *\n * @protected\n * @param {Field} field - The field to get the default value of.\n * @return {*} The default value of the field.\n */\n\n }, {\n key: \"getFieldDefaultValue\",\n value: function getFieldDefaultValue(field) {\n if (!field) {\n return null;\n }\n\n return field.default;\n }\n /**\n * Get the template field that a field should a extend.\n *\n * @param {Field} field - The field to get the template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldTemplate\",\n value: function getFieldTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.extends);\n }\n /**\n * Get the keys of the field's children.\n *\n * @protected\n * @param {Field} field - The field to get the child keys of.\n * @return {array} The keys of the field's children.\n */\n\n }, {\n key: \"getFieldChildrenKeys\",\n value: function getFieldChildrenKeys(field) {\n var i,\n keys = [],\n children = field.children || [];\n\n for (i = 0; i < children.length; i++) {\n keys.push(children[i].pathFragment);\n }\n\n return keys;\n }\n /**\n * Get the template that a field's children should extend.\n *\n * @protected\n * @param {Field} field - The field to get the child template for.\n * @return {Field|null} The template field.\n */\n\n }, {\n key: \"getFieldChildrenTemplate\",\n value: function getFieldChildrenTemplate(field) {\n if (!field) {\n return null;\n }\n\n return this.getField(field.template);\n }\n /**\n * Get the fields dependent upon the given field.\n *\n * @protected\n * @param {Field} field\n * @return {Field[]} The dependent fields\n */\n\n }, {\n key: \"getFieldDependencies\",\n value: function getFieldDependencies(field) {\n var dependencies = this.fieldDependencies[field.path]; // Skip if the field has no dependencies\n\n if (!dependencies || !dependencies.length) {\n return [];\n }\n\n var fields = [];\n\n for (var i = 0; i < dependencies.length; i++) {\n var dependency = this.getField(dependencies[i]);\n\n if (!dependency) {\n continue;\n }\n\n fields.push(dependency);\n }\n\n return fields;\n }\n /**\n * Get the current value of a field.\n *\n * Falls back to default values as appropriate.\n *\n * @public\n * @param {string} path - The path to the field.\n * @return {*} The value of the field\n */\n\n }, {\n key: \"getValue\",\n value: function getValue(path) {\n return this.getFieldValue(this.getField(path));\n }\n /**\n * Cast a value based on the property it belongs to.\n *\n * @public\n * @param {Field} field\n * @param {*} value\n */\n\n }, {\n key: \"castValue\",\n value: function castValue(field, value) {\n if (!field) return value;\n if (!this.casts[field.type]) return value; // if (Array.isArray(value))\n // \treturn value.map(this.casts[field.type]);\n\n value = this.casts[field.type](field, value);\n return value;\n }\n /**\n * Derive a field's value from some data.\n *\n * @protected\n * @param {string} path - The path of the field to derive a value for.\n * @param {Object} data - The data to derive values from.\n * @return {*} The derived value.\n */\n\n }, {\n key: \"deriveValue\",\n value: function deriveValue(path, data) {\n // Return from the value cache if a value is set\n if (this.valueCache.hasOwnProperty(path)) {\n return this.valueCache[path];\n }\n\n var field = this.getField(path);\n var value = this.getFieldValue(field, data); // Return the raw value if there's no such field\n\n if (!field) {\n return value;\n } // Cast the value\n\n\n value = this.castValue(field, value); // Evaluate the field's expression\n\n value = this.evaluateFieldExpression(field, data, value); // Fall back to defaults\n //value = defaultTo(value, defaultTo(field.default, null));\n\n value = this.getFieldValue(field, data, value); // Update the value cache\n\n this.valueCache[path] = value;\n return value;\n }\n /**\n * Build a field's expression.\n *\n * @param {Field} field - The field to build an expression for.\n * @return {Expression} The built expression.\n */\n\n }, {\n key: \"buildFieldExpression\",\n value: function buildFieldExpression(field) {\n if (field.expression == null || typeof field.expression !== 'string') {\n return null;\n } // Use the cached expression if one is available\n\n\n if (this.expressionCache[field.path]) {\n return this.expressionCache[field.path];\n } // Build the initial expression\n\n\n var expression;\n\n try {\n expression = this.parser.parse(field.expression);\n } catch (error) {\n console.error(\"Error parsing expression for field '\".concat(field.path, \"': \").concat(error.message));\n return null;\n } // Substitute contextual variables\n\n\n var substitutions = {\n $parent: field.parent\n };\n\n for (var s in substitutions) {\n try {\n expression = expression.substitute(s, substitutions[s]);\n } catch (error) {\n console.error(\"Error substituting expression variable '\".concat(s, \"' for field '\").concat(field.path, \": \").concat(error.message));\n return null;\n }\n }\n\n this.expressionCache[field.path] = expression;\n return expression;\n }\n /**\n * Evaluate a field's value from its expression.\n *\n * Causes the evaluation of any field dependencies as a result.\n *\n * TODO: Evaluate (and cache) expressions for other field properties! :D\n *\n * @param {Field} field - The field to compute the value of.\n * @param {Object} data - The data to derive values from.\n * @param {*} [value] - The current value of the field.\n * @return {*} The computed value of the field's expression.\n */\n\n }, {\n key: \"evaluateFieldExpression\",\n value: function evaluateFieldExpression(field, data, value) {\n var _this = this;\n\n value = this.getFieldValue(field, data, value); // Parse the expression\n\n var expression = this.buildFieldExpression(field);\n\n if (!expression) {\n return value;\n } // TODO: Extract deriving variables and building contextual functions\n // let variables = buildExpressionContext(field, data, expression, value)?\n // Contextual functions should still update the same \"variables\" reference\n // Derive values for the variables in the expression\n\n\n var variables = expression.variables({\n withMembers: true\n });\n var values = {\n $this: field,\n $value: value\n };\n\n for (var v = 0; v < variables.length; v++) {\n var variable = variables[v];\n if (lodash_has__WEBPACK_IMPORTED_MODULE_6___default()(values, variable)) continue;\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(values, variable, this.deriveValue(variable, data));\n } // Build contextual functions\n\n\n values = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(values, {\n field: function field(path) {\n variables.push(path);\n return _this.getField(path);\n },\n value: function value(path) {\n // Add the path to the list of expression variables\n variables.push(path); // Derive the value for the expression\n\n return _this.deriveValue(path, data);\n }\n }); // Evaluate the expression\n\n try {\n value = expression.evaluate(values);\n } catch (error) {\n console.log('evaluateFieldExpression', field, data, value);\n console.error(\"Error evaluating expression for field '\".concat(field.path, \"': \").concat(error.message));\n } //console.log('evaluateFieldExpression', field.path, expression.toString(), variables, values, value);\n //console.log('evaluateFieldExpression expression', expression);\n // Update the map of field update dependencies\n // TODO: Exclude contextual variables\n // TODO: Move this to an earlier processing step that evaluates the\n // expression with spy functions\n\n\n for (var _v = 0; _v < variables.length; _v++) {\n var _variable = variables[_v];\n this.fieldDependencies[_variable] = this.fieldDependencies[_variable] || [];\n\n if (this.fieldDependencies[_variable].indexOf(field.path) < 0) {\n this.fieldDependencies[_variable].push(field.path);\n }\n }\n\n return value;\n }\n /**\n * Prepare fields from a set of field descriptions.\n *\n * Ascertain's the parent path and path fragment (key) of each field.\n *\n * TODO: Field class, FieldDescription typedef.\n *\n * @protected\n * @param {Field[]} fields - The field description.\n * @returns {Field[]} The given fields prepared with pathFragment and parent properties.\n */\n\n }, {\n key: \"prepareFields\",\n value: function prepareFields(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field, pathFragment, parentPath;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Ascertain a parent path and path fragment\n\n var _splitPath = Object(_functions_splitPath__WEBPACK_IMPORTED_MODULE_24__[\"default\"])(field.path);\n\n var _splitPath2 = _slicedToArray(_splitPath, 2);\n\n parentPath = _splitPath2[0];\n pathFragment = _splitPath2[1];\n field.pathFragment = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.pathFragment, pathFragment);\n field.parent = lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.parent, parentPath);\n }\n\n return fields;\n }\n /**\n * Set the form's fields.\n *\n * TODO: Creates, updates and removes fields accordingly.\n *\n * @public\n * @param {Field[]} fields\n */\n\n }, {\n key: \"setFields\",\n value: function setFields(fields) {\n // Prepare the fields\n this.fields = this.prepareFields(fields); // TODO: Build dictionary from given fields, compare with current fields\n //this.dictionary = this.buildDictionary(this.fields);\n\n this.dictionary = this.updateDictionary(this.fields); // Compose the fields into a tree\n\n this.tree = this.buildTree(this.dictionary); // Clear all caches\n\n this.valueCache = {}; //this.expressionCache = {};\n\n this.fieldDependencies = {};\n }\n /**\n * Add a form field.\n *\n * @param {Field} field\n */\n\n }, {\n key: \"addField\",\n value: function addField(field) {\n var parent = this.getFieldParent(field);\n\n if (!parent) {\n console.warn(\"Could not set field \".concat(field.path, \" - its parent does not exist\"));\n return;\n }\n\n if (!parent.children) {\n parent.children = [];\n }\n\n if (!parent.children.includes(field)) {\n parent.children.push(field);\n }\n\n this.dictionary[field.path] = field;\n }\n /**\n * Update a property with the given value.\n *\n * @public\n * @param {Object} data - The data to update.\n * @param {string} path - The path of the field to update.\n * @param {*} value - The value to set.\n * @return {*} The updated value\n */\n\n }, {\n key: \"setValue\",\n value: function setValue(data, path, value) {\n var field = this.getField(path); // Update the value if one is given\n\n if (value !== undefined) {\n if (field) {\n field.value = value;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, value);\n } // Update the field at this path\n\n\n this.updatePath(path, data); // Get the updated value\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path);\n }\n /**\n * Clear the value cache.\n *\n * Optionally accepts a path to clear.\n *\n * @param {string} path\n */\n\n }, {\n key: \"clearValueCache\",\n value: function clearValueCache() {\n var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';\n\n // Just empty the entire cache if there's no path is or there's no field\n // at the given path\n if (!path || !this.getField(path)) {\n this.valueCache = {};\n return;\n }\n\n var i,\n field = this.getField(path); // Clear cached values of child fields iteratively\n\n var child,\n children = field.children,\n nextChildren = [];\n\n while (children && children.length) {\n nextChildren = [];\n\n for (i = 0; i < children.length; i++) {\n child = children[i];\n delete this.valueCache[child.path];\n\n if (child.children) {\n nextChildren = nextChildren.concat(child.children);\n }\n }\n\n children = nextChildren;\n } // Clear the cached value for this field\n\n\n delete this.valueCache[field.path]; // Clear cached values of parent fields iteratively\n\n var ancestors = this.getFieldAncestors(field);\n\n for (i = 0; i < ancestors.length; i++) {\n delete this.valueCache[ancestors[i].path];\n }\n }\n /**\n * Derive a property's name from its path.\n *\n * @protected\n * @param {Field} field\n * @return {string} The derived name\n */\n\n }, {\n key: \"deriveName\",\n value: function deriveName(field) {\n var path = field.path;\n var lastDotIndex = path.lastIndexOf('.');\n return _mixins_util__WEBPACK_IMPORTED_MODULE_17__[\"util\"].sentenceCase(path.substring(lastDotIndex + 1));\n }\n /**\n * Set the default field properties for each type.\n *\n * @param {Object.} fieldProperties - The default field properties, keyed by field type.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(fieldProperties) {\n this.defaults = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.defaults, fieldProperties);\n }\n /**\n * Apply the form's default properties to the given fields.\n *\n * Fills in default values, derives default names.\n *\n * @protected\n * @param {Field[]} fields - The fields to apply default values to.\n * @returns {Field[]}\n */\n\n }, {\n key: \"applyDefaults\",\n value: function applyDefaults(fields) {\n if (!fields || !fields.length) {\n return fields;\n }\n\n var i, field;\n\n for (i = 0; i < fields.length; i++) {\n field = fields[i]; // Derive a name\n\n if (field.name === undefined) {\n field.name = this.deriveName(field);\n } // Apply global defaults\n\n\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults['*']); // Apply type-specific defaults\n\n if (this.defaults[field.type]) {\n field = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field, this.defaults[field.type]);\n } // Apply default input options\n\n\n if (this.inputOptions[field.input]) {\n field.options = field.options || {};\n field.options = lodash_defaultsDeep__WEBPACK_IMPORTED_MODULE_3___default()(field.options, this.inputOptions[field.input]);\n } // Disable the field implicitly if it has an expression\n\n\n if (!field.hasOwnProperty('disabled')) {\n field.disabled = !!field.expression;\n }\n }\n\n return fields;\n }\n /**\n * Add to the form's functions.\n *\n * @param {Object.} functions - Functions to add, keyed by name.\n */\n\n }, {\n key: \"addFunctions\",\n value: function addFunctions(functions) {\n // Add the functions to the form\n this.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.functions, functions); // Add the functions to the expression parser\n\n this.parser.functions = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(this.parser.functions, this.functions); // Clear the expression cache\n //this.expressionCache = {};\n }\n /**\n * Update the form using the given data.\n *\n * @public\n * @param {Object} [data] - The data to update with.\n */\n\n }, {\n key: \"update\",\n value: function update(data) {\n // Clear updated fields map\n this.updatedFields = {};\n console.time('buildData');\n var formData = this.buildData(this.tree);\n console.timeEnd('buildData');\n console.log(formData);\n console.time('diff');\n var diff = Object(deep_object_diff__WEBPACK_IMPORTED_MODULE_27__[\"detailedDiff\"])(formData, data);\n console.timeEnd('diff');\n console.log(diff);\n console.time('flatten');\n var addedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.added);\n var updatedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.updated);\n var deletedPaths = flat__WEBPACK_IMPORTED_MODULE_26___default()(diff.deleted);\n console.timeEnd('flatten');\n console.log(addedPaths, updatedPaths, deletedPaths); //let path;\n // for (path in addedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // for (path in updatedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n //\n // for (path in deletedPaths) {\n // \tthis.updatePath(path, data, BOTH);\n // }\n // Update the value of every field\n\n this.updatePath('', data);\n console.time('traverseTree');\n Object(_functions_traverseTree__WEBPACK_IMPORTED_MODULE_23__[\"default\"])(this.tree, function (field) {// Pre-order\n }, function (field) {// Post-order\n //console.log(field.path);\n });\n console.timeEnd('traverseTree');\n }\n /**\n * Update the field at the given path.\n *\n * @param {string} path - The path of the field to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updatePath\",\n value: function updatePath(path, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n this.updateField(this.getField(path), data, direction);\n }\n /**\n * Update the given fields with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field[]} fields - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateFields\",\n value: function updateFields(fields, data, direction) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.updateField(fields[i], data, direction);\n }\n }\n /**\n * Update the given field with the given data.\n *\n * Recursively descends into child fields and updates dependent fields,\n * including parents.\n *\n * @protected\n * @param {Field} field - The fields to update.\n * @param {Object} data - The data to update with.\n * @param {number} [direction=BOTH] - Update direction (UP: -1, BOTH: 0, DOWN: 1)\n */\n\n }, {\n key: \"updateField\",\n value: function updateField(field, data) {\n var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : BOTH;\n\n if (!field) {\n return;\n } //console.log('updateField()', field.path);\n //console.count('updateField()');\n\n\n this.clearValueCache(field.path); // Update the field's children\n\n if (direction >= 0) {\n // TODO: This would be a good spot for an event to fire to allow plugins\n // (like inheritance) to intercept child update behaviour\n this.updateFieldInheritance(field, data);\n\n if (!field.omit) {\n this.updateFields(field.children, data);\n }\n } // Skip if this field has already been updated\n\n\n if (this.updatedFields[field.path]) {\n return;\n } // Apply default values\n\n\n this.applyDefaults([field]); // Update the state's value\n\n this.updateDataValue(field, data); // Update the field's value (and update all fields dependent on this one)\n\n this.updateFieldValue(field, data, direction <= 0); // Mark the field as updated\n\n this.updatedFields[field.path] = true;\n }\n /**\n * Update data from the given field.\n *\n * @protected\n * @param {Field} field - The field to update with.\n * @param {Object} data - The data to update.\n * @return {Object} The updated data.\n */\n\n }, {\n key: \"updateDataValue\",\n value: function updateDataValue(field, data) {\n if (!field || field.omit || field.virtual) {\n return data;\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, this.deriveValue(field.path, data));\n return data;\n }\n /**\n * Update a field using the given data.\n *\n * @protected\n * @param {Field} field - The field to update.\n * @param {Object} data - The data to update with.\n * @param {boolean=false} updateParents - Whether to update the field's parents.\n */\n\n }, {\n key: \"updateFieldValue\",\n value: function updateFieldValue(field, data) {\n var updateParents = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!field) {\n return;\n } // Update the field value\n\n\n field.value = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, field.path); // Update all fields dependent on this one\n\n this.updateFieldDependencies(field, data, updateParents);\n }\n /**\n * Update fields that are dependent upon the value of the given field.\n *\n * @protected\n * @param {Field} field - The field to update dependencies of.\n * @param {Object} data - The data to update from.\n * @param {boolean=false} updateAncestors - Whether to update the field's ancestors.\n */\n\n }, {\n key: \"updateFieldDependencies\",\n value: function updateFieldDependencies(field, data, updateAncestors) {\n // Recursively update parent field values\n if (updateAncestors) {\n this.updateFieldAncestorValues(field, data);\n } // Update fields listed as dependencies\n\n\n this.updateFields(this.getFieldDependencies(field), data);\n }\n /**\n * Update the ancestors of the given field.\n *\n * @protected\n * @param {Field} field - The field to update parents of.\n * @param {Object} data - The data to update with.\n */\n\n }, {\n key: \"updateFieldAncestorValues\",\n value: function updateFieldAncestorValues(field, data) {\n var i,\n parents = this.getFieldAncestors(field);\n\n for (i = 0; i < parents.length; i++) {\n this.updateFieldValue(parents[i], data);\n }\n }\n /**\n * Update a field's inheritance.\n *\n * Ensures that inherited child fields exist.\n *\n * @param {Field} field - The field to update the inheritance of.\n * @param {Object} data - The date to update with.\n */\n\n }, {\n key: \"updateFieldInheritance\",\n value: function updateFieldInheritance(field, data) {\n // Update child template fields\n this.updateTemplateFields(field, data); // Inherit the field's template\n\n this.inheritTemplate(field, data);\n }\n /**\n * Get the keys of child fields that don't exist in the given data.\n *\n * Retrieves the new keys, existing keys and old keys of a field compared\n * to its data.\n *\n * @protected\n * @param {Field} field - The field with a template.\n * @param {Object} data - The data to diff against.\n * @return array [newPaths[], existingPaths[], oldPaths[]]\n */\n\n }, {\n key: \"diffFieldDataKeys\",\n value: function diffFieldDataKeys(field, data) {\n // Grab the data keys and child field keys\n var childData = this.getFieldValue(field, data);\n var childDataKeys = childData ? Object.keys(childData) : [];\n var childFieldKeys = this.getFieldChildrenKeys(field); // Keys in data that aren't in fields\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childDataKeys, childFieldKeys); // Keys in data and fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(childDataKeys, childFieldKeys); // Keys in fields that aren't in data\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(childFieldKeys, childDataKeys); // console.log('diffTemplateFieldKeys()', field.path, 'newKeys', newKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'existingKeys', existingKeys);\n // console.log('diffTemplateFieldKeys()', field.path, 'oldKeys', oldKeys);\n\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Unravel all templates into fields for the given field and data.\n *\n * TODO: Try to merge this into inheritTemplate(), or extract a method\n * that can handle both cases, like updateFieldChildrenInheritance().\n *\n * @protected\n * @param {Field} field - The field to update template fields for.\n * @param {Object} [data] - The data used to unravel field templates.\n */\n\n }, {\n key: \"updateTemplateFields\",\n value: function updateTemplateFields(field, data) {\n if (!field) {\n return;\n }\n\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return;\n }\n\n var dictionary = this.dictionary,\n i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Find child fields that need to be added, updated or removed\n\n var _this$diffFieldDataKe = this.diffFieldDataKeys(field, data),\n _this$diffFieldDataKe2 = _slicedToArray(_this$diffFieldDataKe, 3),\n newKeys = _this$diffFieldDataKe2[0],\n existingKeys = _this$diffFieldDataKe2[1],\n oldKeys = _this$diffFieldDataKe2[2];\n\n var existingFieldKeys = this.getFieldChildrenKeys(field);\n var fixedKeys = field.fixed || []; // TODO: Extract to... diffFixedFieldDataKeys()...?\n // Remove fixed keys from the keys that need removing\n\n oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(oldKeys, fixedKeys); // Add the existent fixed keys to the keys that need updating\n\n existingKeys = existingKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(fixedKeys, existingFieldKeys), existingKeys, newKeys)); // Add the non-existent fixed keys to the keys that need creating\n\n newKeys = newKeys.concat(lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(fixedKeys, existingFieldKeys, newKeys)); //console.log(field.path, newKeys, existingKeys, oldKeys, fixedKeys, existingFieldKeys);\n // Remove old fields\n\n for (i = 0; i < oldKeys.length; i++) {\n key = oldKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n this.removeField(this.getField(path));\n } // Update existing fields\n\n\n for (i = 0; i < existingKeys.length; i++) {\n key = existingKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() existingField', field.path, path);\n // Ensure the existing field has the correct template\n\n existingField = this.getField(path);\n existingField.extends = template.path;\n } // Build new fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field);\n\n for (i = 0; i < newKeys.length; i++) {\n key = newKeys[i];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('updateTemplateFields() newField', path, value[key]);\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: template.path\n };\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields);\n }\n\n this.updateDictionary(newFields);\n }\n /**\n * Diff the keys of the first field's children with those of the second\n * field's children.\n *\n * Finds keys of the first that aren't of the second, keys that are of both,\n * and keys of the second that aren't of the first.\n *\n * @protected\n * @param {Field} firstField\n * @param {Field} secondField\n * @returns {array} [newKeys, existingKeys, oldKeys]\n */\n\n }, {\n key: \"diffFieldChildrenKeys\",\n value: function diffFieldChildrenKeys(firstField, secondField) {\n var firstFieldKeys = this.getFieldChildrenKeys(firstField);\n var secondFieldKeys = this.getFieldChildrenKeys(secondField); // Child keys of the first field that aren't of the second field\n\n var newKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(firstFieldKeys, secondFieldKeys); // Keys in the children of both fields\n\n var existingKeys = lodash_intersection__WEBPACK_IMPORTED_MODULE_12___default()(firstFieldKeys, secondFieldKeys); // Child keys of the second field that aren't of the first\n\n var oldKeys = lodash_difference__WEBPACK_IMPORTED_MODULE_11___default()(secondFieldKeys, firstFieldKeys);\n return [newKeys, existingKeys, oldKeys];\n }\n /**\n * Apply a field's inheritance.\n *\n * Ensures that a field inherits from its base field.\n *\n * @param {Field} field - The inheriting field.\n * @param {Object} data - The data to update with.\n * @return {Field}\n */\n\n }, {\n key: \"inheritTemplate\",\n value: function inheritTemplate(field, data) {\n if (!field) {\n return field;\n }\n\n var template = this.getFieldTemplate(field); // Skip fields without a template\n\n if (!template) {\n return field;\n } // Inherit the template\n\n\n if (field.extended !== template.path) {\n var templateClone = lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(template); // We don't want to inherit children, nor do we support more than a\n // single layer of inheritance\n\n delete templateClone.children;\n delete templateClone.template; // if (field.path === 'skills.list.test.ability') {\n // \tconsole.log('skills.list.test.ability', clone(field), templateClone);\n // }\n // Merge sandwich to retain original values\n\n field = lodash_merge__WEBPACK_IMPORTED_MODULE_2___default()(field, templateClone, lodash_clone__WEBPACK_IMPORTED_MODULE_1___default()(field));\n\n if (field.name == null) {\n field.name = this.deriveName(field);\n }\n\n field.extended = template.path;\n this.prepareFields([field]);\n } //console.log('inheritTemplate()', field.path, template.path);\n // Update child field inheritance\n\n\n var i,\n key,\n path,\n value,\n defaultValue,\n existingField,\n newField,\n newFields = []; // Diff field child keys and template child keys\n\n var _this$diffFieldChildr = this.diffFieldChildrenKeys(template, field),\n _this$diffFieldChildr2 = _slicedToArray(_this$diffFieldChildr, 2),\n newKeys = _this$diffFieldChildr2[0],\n existingKeys = _this$diffFieldChildr2[1]; // Update existing template fields\n\n\n for (var _i2 = 0; _i2 < existingKeys.length; _i2++) {\n key = existingKeys[_i2];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key);\n existingField = this.getField(path); //console.log('inheritTemplate() existingField', path, existingField);\n\n existingField.extends = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key);\n } // Build new template fields\n\n\n value = this.getFieldValue(field, data);\n defaultValue = this.getFieldDefaultValue(field); //console.log('inheritTemplate()', field.path, value);\n\n for (var _i3 = 0; _i3 < newKeys.length; _i3++) {\n key = newKeys[_i3];\n path = Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(field.path, key); //console.log('inherit newKey', path, value[key]);\n // Build the new field\n\n newField = {\n path: path,\n pathFragment: key,\n parent: field.path,\n extends: Object(_functions_joinPath__WEBPACK_IMPORTED_MODULE_25__[\"default\"])(template.path, key)\n }; // Propagate values\n\n if (value != null && value.hasOwnProperty(key)) {\n newField.value = value[key];\n }\n\n if (defaultValue != null && defaultValue.hasOwnProperty(key)) {\n newField.default = defaultValue[key];\n }\n\n newFields.push(newField);\n } // Add the new fields to the parent field and dictionary\n // TODO: Extract addFieldChildren(field, children)\n\n\n if (newFields.length) {\n field.children = field.children || [];\n field.children = field.children.concat(newFields); // We then want to sort these in the order of template's children\n // TODO: Extract sortFieldChildren(field)\n\n var templateChildKeys = this.getFieldChildrenKeys(template); // We flip the child keys into an object where the values are the\n // indices of the child keys array\n\n templateChildKeys = lodash_zipObject__WEBPACK_IMPORTED_MODULE_15___default()(templateChildKeys, _toConsumableArray(templateChildKeys.keys())); // This makes it easier to sort\n\n field.children = lodash_sortBy__WEBPACK_IMPORTED_MODULE_16___default()(field.children, function (child) {\n return templateChildKeys[child.pathFragment];\n });\n }\n\n this.updateDictionary(newFields);\n return field;\n }\n /**\n * Remove data from the given path.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path to remove.\n */\n\n }, {\n key: \"removeValue\",\n value: function removeValue(data, path) {\n this.removePath(path, data);\n }\n /**\n * Remove data at the given path and update parent fields.\n *\n * TODO: The naming and existence of this method doesn't quite make sense.\n * You'd expect it to remove the field(s) too.\n * Refactor!\n *\n * @protected\n * @param {string} path - The path to remove.\n * @param {Object} data - The data to remove the path from.\n */\n\n }, {\n key: \"removePath\",\n value: function removePath(path, data) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Remove the state data\n\n\n this.removeData(field, data); // Update the parent field\n\n this.updateField(this.getFieldParent(field), data);\n }\n /**\n * Remove the given fields.\n *\n * @protected\n * @param {Field[]} fields - The fields to remove.\n */\n\n }, {\n key: \"removeFields\",\n value: function removeFields(fields) {\n if (!Array.isArray(fields) || !fields.length) {\n return;\n }\n\n for (var i = 0; i < fields.length; i++) {\n this.removeField(fields[i]);\n }\n }\n /**\n * Remove a field.\n *\n * Clears dictionary and parent references to the field.\n *\n * Doesn't remove data or update parent field values.\n *\n * @protected\n * @param {Field} field - The field to remove.\n */\n\n }, {\n key: \"removeField\",\n value: function removeField(field) {\n if (!field) {\n return;\n }\n\n this.clearValueCache(field.path); // Remove the field's children\n\n this.removeFields(field.children); // Remove the field from the dictionary and dependency list\n\n delete this.dictionary[field.path];\n delete this.fieldDependencies[field.path]; // Remove the field from its parent\n\n var parent = this.getFieldParent(field);\n lodash_pull__WEBPACK_IMPORTED_MODULE_9___default()(parent.children, field);\n }\n /**\n * Remove a field's path from the given data.\n *\n * @param {Field} field - The field whose path to remove from data.\n * @param {Object} data - The data to remove from.\n */\n\n }, {\n key: \"removeData\",\n value: function removeData(field, data) {\n if (!field) {\n return;\n }\n\n var parent = this.getField(field.parent);\n\n if (!parent) {\n return;\n }\n\n var parentPath = parent.path;\n var parentValue = this.getFieldValue(parent, data);\n var key = field.pathFragment;\n\n if (Array.isArray(parentValue)) {\n parentValue.splice(key, 1);\n } else {\n delete parentValue[key];\n }\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, parentPath, parentValue);\n }\n /**\n * Update the dictionary with the given fields.\n *\n * @param {Field[]} fields\n * @returns {FieldDictionary}\n */\n\n }, {\n key: \"updateDictionary\",\n value: function updateDictionary(fields) {\n if (!Array.isArray(fields)) {\n return this.dictionary;\n }\n\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i] || !fields[i].path) {\n continue;\n }\n\n this.dictionary[fields[i].path] = fields[i];\n }\n\n return this.dictionary;\n }\n /**\n * Build a dictionary from the given fields.\n *\n * @param {Field[]} fields\n * @return {FieldDictionary}\n */\n\n }, {\n key: \"buildDictionary\",\n value: function buildDictionary(fields) {\n return Object(_functions_buildDictionary__WEBPACK_IMPORTED_MODULE_21__[\"default\"])(fields);\n }\n /**\n * Build a tree from the given dictionary.\n *\n * @protected\n * @param {FieldDictionary} dictionary\n * @returns {Field}\n */\n\n }, {\n key: \"buildTree\",\n value: function buildTree(dictionary) {\n return Object(_functions_buildTree__WEBPACK_IMPORTED_MODULE_22__[\"default\"])(dictionary);\n }\n /**\n * Build data from the current field state.\n *\n * @param {Field} field - The root field to traverse from.\n * @param {Object} [data={}] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildData\",\n value: function buildData(field) {\n var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (!field) {\n return data;\n }\n\n var child, childData; // If the field has children, build the data of its children\n\n if (field.children) {\n for (var c = 0; c < field.children.length; c++) {\n child = field.children[c];\n\n if (child.virtual || child.omit) {\n continue;\n }\n\n childData = this.buildData(field.children[c], data);\n }\n\n return data;\n } // Set data\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, field.path, lodash_defaultTo__WEBPACK_IMPORTED_MODULE_10___default()(field.value, field.default));\n return data;\n }\n /**\n * Build data for a field's template.\n *\n * @protected\n * @param {Field} field - The field to build child data for.\n * @param {Object} [data] - The target data object.\n * @return {Object} The built data.\n */\n\n }, {\n key: \"buildTemplateData\",\n value: function buildTemplateData(field, data) {\n var template = this.getFieldChildrenTemplate(field);\n\n if (!template) {\n return null;\n }\n\n return lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(this.buildData(template), template.path);\n }\n /**\n * Add new child data for the field at the given path using its template.\n *\n * @public\n * @param {Object} data - The data to change.\n * @param {string} path - The path of the field to add new child data to.\n * @param {string|number} [key] - Optional key to use for the new child data.\n */\n\n }, {\n key: \"addItem\",\n value: function addItem(data, path, key) {\n var field = this.getField(path);\n\n if (!field) {\n return;\n } // Build the new child data\n\n\n var newData = this.buildTemplateData(field); // Get the target for the data\n\n var target = lodash_get__WEBPACK_IMPORTED_MODULE_7___default()(data, path, []); // Add the new child data to the collection\n\n if (Array.isArray(target)) {\n target.push(newData);\n } else if (key != null && _typeof(target) === 'object') {\n target[key] = newData;\n } else {\n // Bail if we're not dealing with a collection\n console.warn(\"Could not create new child data for '\".concat(path, \"';\") + \" either it wasn't an array or wasn't an object with a key provided\");\n return;\n } // Set it back\n\n\n lodash_set__WEBPACK_IMPORTED_MODULE_8___default()(data, path, target); // Update the form\n\n this.updatePath(path, data);\n }\n }]);\n\n return FormProcessor;\n}();\n/**\n * A dictionary of fields.\n *\n * Fields are keyed by their path.\n *\n * @typedef {Object.} FieldDictionary\n */\n\n/**\n * A field description.\n *\n * TODO: Update this to reflect the simplest approach to describing fields.\n * Could also be called FieldOptions if passed to the constructor of a\n * Field class.\n *\n * @typedef {Object} FieldDescription\n */\n\n/**\n * A field.\n *\n * TODO: Formalise as a class?\n *\n * @typedef {Object} Field\n *\n * @property {string} path - The path of the field.\n * @property {string} [parent] - The path of the field's parent, if any. Overrides the parent that would otherwise be determined from the `path`.\n * @property {string} [pathFragment] - The leaf of the field's path. TODO: Rename to key\n * @property {string} [type] - The type of the field. Determines the type of value to read and store. Defaults to `'number'`.\n * @property {string} [input] - The input type to use for this field, if any. // TODO: Rename? Might not be an actual input... (i.e. section). `element` might be a good name.\n * @property {Object} [options] - The input options. A free-form object for different input types to interpret and utilise.\n * @property {string} [name] - The field's name. Defaults to a sentence-case translation of the field's key. TODO: Rename to label?\n * @property {string} [description] - The field's description.\n * @property {boolean} [omit=false] - Whether to prevent storing the property's value in data AND prevent updating any children. Defaults to `false`.\n * @property {boolean} [virtual=false] - Whether to prevent storing the property's value in data. Defaults to `false`.\n * @property {string|boolean} [visible=true] - Whether the property is visible. Defaults to `true`. String values are interpreted as expressions.\n * @property {string|boolean} [disabled=false] - Whether the property is disabled. Defaults to `true` if `expression` is set, otherwise defaults to `false`. String values are interpreted as expressions. TODO: Input options?\n * @property {*} [value] - The field's value.\n * @property {*} [default] - The field's default value. Defaults appropriately for the set `type`.\n * @property {boolean} [merge] - Whether to merge the field's non-scalar value with its default value.\n * @property {string} [expression] - An expression used to compute the field's value. Implies `disabled` when set.\n * @property {string} [validator] - The field's validation function. Defaults as appropriate to the `type`.\n * @property {string} [extends] - The path of a field to inherit.\n * @property {string} [extended] - The path of a field that been inherited.\n * @property {string} [mirror] - The path of a field to mirror. TODO: Implement\n * @property {Field[]} [children] - Child fields.\n * @property {string} [template] - Template field that all child fields should extend. Can be a `Field` or a `path` to a field.\n * @property {Object|array} [fixed] - A map or list of child keys that cannot be removed at runtime, if present.\n */\n\n\n\n\n//# sourceURL=webpack://%5Bname%5D/./src/services/FormProcessor.js?"); /***/ }), diff --git a/src/services/FormProcessor.js b/src/services/FormProcessor.js index d0d06b7..f4c4420 100644 --- a/src/services/FormProcessor.js +++ b/src/services/FormProcessor.js @@ -203,6 +203,15 @@ export default class FormProcessor * @type {Object.} */ this.fieldDependencies = {}; + + /** + * Map of updated fields. + * + * Used to prevent updating fields more than once. + * + * @type {Object.} + */ + this.updatedFields = {}; } /** @@ -663,7 +672,7 @@ export default class FormProcessor // Clear all caches this.valueCache = {}; - this.expressionCache = {}; + //this.expressionCache = {}; this.fieldDependencies = {}; } @@ -862,7 +871,7 @@ export default class FormProcessor this.parser.functions = merge(this.parser.functions, this.functions); // Clear the expression cache - this.expressionCache = {}; + //this.expressionCache = {}; } /** @@ -873,11 +882,8 @@ export default class FormProcessor */ update(data) { - // Update the value of every field - this.updatePath('', data); - - // TODO: Diff any *paths* that changed and update those - // i.e. implement diffFieldDataPaths() + // Clear updated fields map + this.updatedFields = {}; console.time('buildData'); let formData = this.buildData(this.tree); @@ -912,6 +918,9 @@ export default class FormProcessor // this.updatePath(path, data, BOTH); // } + // Update the value of every field + this.updatePath('', data); + console.time('traverseTree'); traverseTree( this.tree, @@ -919,7 +928,7 @@ export default class FormProcessor }, (field) => { // Post-order - console.log(field.path); + //console.log(field.path); } ); console.timeEnd('traverseTree'); @@ -977,8 +986,7 @@ export default class FormProcessor } //console.log('updateField()', field.path); - - console.count('updateField()'); + //console.count('updateField()'); this.clearValueCache(field.path); @@ -993,6 +1001,11 @@ export default class FormProcessor } } + // Skip if this field has already been updated + if (this.updatedFields[field.path]) { + return; + } + // Apply default values this.applyDefaults([field]); @@ -1001,6 +1014,9 @@ export default class FormProcessor // Update the field's value (and update all fields dependent on this one) this.updateFieldValue(field, data, direction <= 0); + + // Mark the field as updated + this.updatedFields[field.path] = true; } /** @@ -1047,15 +1063,15 @@ export default class FormProcessor * Update fields that are dependent upon the value of the given field. * * @protected - * @param {Field} field - The field to update dependencies of. - * @param {Object} data - The data to update from. - * @param {boolean=false} updateParents - Whether to update the field's parents. + * @param {Field} field - The field to update dependencies of. + * @param {Object} data - The data to update from. + * @param {boolean=false} updateAncestors - Whether to update the field's ancestors. */ - updateFieldDependencies(field, data, updateParents) + updateFieldDependencies(field, data, updateAncestors) { // Recursively update parent field values - if (updateParents) { - this.updateFieldAncestors(field, data); + if (updateAncestors) { + this.updateFieldAncestorValues(field, data); } // Update fields listed as dependencies @@ -1069,7 +1085,7 @@ export default class FormProcessor * @param {Field} field - The field to update parents of. * @param {Object} data - The data to update with. */ - updateFieldAncestors(field, data) + updateFieldAncestorValues(field, data) { let i, parents = this.getFieldAncestors(field);