UNPKG

angular6-json-schema-form

Version:
1,341 lines (1,338 loc) 560 kB
import { __values, __spread, __assign, __read, __decorate, __metadata, __param, __extends } from 'tslib'; import cloneDeep from 'lodash-es/cloneDeep'; import Ajv from 'ajv'; import jsonDraft6 from 'ajv/lib/refs/json-schema-draft-06.json'; import filter from 'lodash-es/filter'; import map$1 from 'lodash-es/map'; import { FormGroup, FormArray, FormControl, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { from, Observable, forkJoin, Subject } from 'rxjs'; import { Injectable, Input, Component, ChangeDetectionStrategy, ViewChild, ViewContainerRef, ComponentFactoryResolver, Inject, forwardRef, Output, ChangeDetectorRef, EventEmitter, Directive, ElementRef, NgZone, NgModule, Optional } from '@angular/core'; import isEqual$1 from 'lodash-es/isEqual'; import { map } from 'rxjs/operators'; import uniqueId from 'lodash-es/uniqueId'; import { DomSanitizer } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; import { MAT_LABEL_GLOBAL_OPTIONS, MatNativeDateModule } from '@angular/material/core'; import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldModule } from '@angular/material/form-field'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MediaMarshaller } from '@angular/flex-layout/core'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatChipsModule } from '@angular/material/chips'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSliderModule } from '@angular/material/slider'; import { MatStepperModule } from '@angular/material/stepper'; import { MatTabsModule } from '@angular/material/tabs'; import { MatTooltipModule } from '@angular/material/tooltip'; /** * '_executeValidators' utility function * * Validates a control against an array of validators, and returns * an array of the same length containing a combination of error messages * (from invalid validators) and null values (from valid validators) * * // { AbstractControl } control - control to validate * // { IValidatorFn[] } validators - array of validators * // { boolean } invert - invert? * // { PlainObject[] } - array of nulls and error message */ function _executeValidators(control, validators, invert) { if (invert === void 0) { invert = false; } return validators.map(function (validator) { return validator(control, invert); }); } /** * '_executeAsyncValidators' utility function * * Validates a control against an array of async validators, and returns * an array of observabe results of the same length containing a combination of * error messages (from invalid validators) and null values (from valid ones) * * // { AbstractControl } control - control to validate * // { AsyncIValidatorFn[] } validators - array of async validators * // { boolean } invert - invert? * // - array of observable nulls and error message */ function _executeAsyncValidators(control, validators, invert) { if (invert === void 0) { invert = false; } return validators.map(function (validator) { return validator(control, invert); }); } /** * '_mergeObjects' utility function * * Recursively Merges one or more objects into a single object with combined keys. * Automatically detects and ignores null and undefined inputs. * Also detects duplicated boolean 'not' keys and XORs their values. * * // { PlainObject[] } objects - one or more objects to merge * // { PlainObject } - merged object */ function _mergeObjects() { var e_1, _a, e_2, _b; var objects = []; for (var _i = 0; _i < arguments.length; _i++) { objects[_i] = arguments[_i]; } var mergedObject = {}; try { for (var objects_1 = __values(objects), objects_1_1 = objects_1.next(); !objects_1_1.done; objects_1_1 = objects_1.next()) { var currentObject = objects_1_1.value; if (isObject(currentObject)) { try { for (var _c = __values(Object.keys(currentObject)), _d = _c.next(); !_d.done; _d = _c.next()) { var key = _d.value; var currentValue = currentObject[key]; var mergedValue = mergedObject[key]; mergedObject[key] = !isDefined(mergedValue) ? currentValue : key === 'not' && isBoolean(mergedValue, 'strict') && isBoolean(currentValue, 'strict') ? xor(mergedValue, currentValue) : getType(mergedValue) === 'object' && getType(currentValue) === 'object' ? _mergeObjects(mergedValue, currentValue) : currentValue; } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_d && !_d.done && (_b = _c.return)) _b.call(_c); } finally { if (e_2) throw e_2.error; } } } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (objects_1_1 && !objects_1_1.done && (_a = objects_1.return)) _a.call(objects_1); } finally { if (e_1) throw e_1.error; } } return mergedObject; } /** * '_mergeErrors' utility function * * Merges an array of objects. * Used for combining the validator errors returned from 'executeValidators' * * // { PlainObject[] } arrayOfErrors - array of objects * // { PlainObject } - merged object, or null if no usable input objectcs */ function _mergeErrors(arrayOfErrors) { var mergedErrors = _mergeObjects.apply(void 0, __spread(arrayOfErrors)); return isEmpty(mergedErrors) ? null : mergedErrors; } /** * 'isDefined' utility function * * Checks if a variable contains a value of any type. * Returns true even for otherwise 'falsey' values of 0, '', and false. * * // value - the value to check * // { boolean } - false if undefined or null, otherwise true */ function isDefined(value) { return value !== undefined && value !== null; } /** * 'hasValue' utility function * * Checks if a variable contains a value. * Returs false for null, undefined, or a zero-length strng, '', * otherwise returns true. * (Stricter than 'isDefined' because it also returns false for '', * though it stil returns true for otherwise 'falsey' values 0 and false.) * * // value - the value to check * // { boolean } - false if undefined, null, or '', otherwise true */ function hasValue(value) { return value !== undefined && value !== null && value !== ''; } /** * 'isEmpty' utility function * * Similar to !hasValue, but also returns true for empty arrays and objects. * * // value - the value to check * // { boolean } - false if undefined, null, or '', otherwise true */ function isEmpty(value) { if (isArray(value)) { return !value.length; } if (isObject(value)) { return !Object.keys(value).length; } return value === undefined || value === null || value === ''; } /** * 'isString' utility function * * Checks if a value is a string. * * // value - the value to check * // { boolean } - true if string, false if not */ function isString(value) { return typeof value === 'string'; } /** * 'isNumber' utility function * * Checks if a value is a regular number, numeric string, or JavaScript Date. * * // value - the value to check * // { any = false } strict - if truthy, also checks JavaScript tyoe * // { boolean } - true if number, false if not */ function isNumber(value, strict) { if (strict === void 0) { strict = false; } if (strict && typeof value !== 'number') { return false; } return !isNaN(value) && value !== value / 0; } /** * 'isInteger' utility function * * Checks if a value is an integer. * * // value - the value to check * // { any = false } strict - if truthy, also checks JavaScript tyoe * // {boolean } - true if number, false if not */ function isInteger(value, strict) { if (strict === void 0) { strict = false; } if (strict && typeof value !== 'number') { return false; } return !isNaN(value) && value !== value / 0 && value % 1 === 0; } /** * 'isBoolean' utility function * * Checks if a value is a boolean. * * // value - the value to check * // { any = null } option - if 'strict', also checks JavaScript type * if TRUE or FALSE, checks only for that value * // { boolean } - true if boolean, false if not */ function isBoolean(value, option) { if (option === void 0) { option = null; } if (option === 'strict') { return value === true || value === false; } if (option === true) { return value === true || value === 1 || value === 'true' || value === '1'; } if (option === false) { return value === false || value === 0 || value === 'false' || value === '0'; } return value === true || value === 1 || value === 'true' || value === '1' || value === false || value === 0 || value === 'false' || value === '0'; } function isFunction(item) { return typeof item === 'function'; } function isObject(item) { return item !== null && typeof item === 'object' && Object.prototype.toString.call(item) === '[object Object]'; } function isArray(item) { return Array.isArray(item) || Object.prototype.toString.call(item) === '[object Array]'; } function isDate(item) { return typeof item === 'object' && Object.prototype.toString.call(item) === '[object Date]'; } function isMap(item) { return typeof item === 'object' && Object.prototype.toString.call(item) === '[object Map]'; } function isSet(item) { return typeof item === 'object' && Object.prototype.toString.call(item) === '[object Set]'; } /** * 'getType' function * * Detects the JSON Schema Type of a value. * By default, detects numbers and integers even if formatted as strings. * (So all integers are also numbers, and any number may also be a string.) * However, it only detects true boolean values (to detect boolean values * in non-boolean formats, use isBoolean() instead). * * If passed a second optional parameter of 'strict', it will only detect * numbers and integers if they are formatted as JavaScript numbers. * * Examples: * getType('10.5') = 'number' * getType(10.5) = 'number' * getType('10') = 'integer' * getType(10) = 'integer' * getType('true') = 'string' * getType(true) = 'boolean' * getType(null) = 'null' * getType({ }) = 'object' * getType([]) = 'array' * * getType('10.5', 'strict') = 'string' * getType(10.5, 'strict') = 'number' * getType('10', 'strict') = 'string' * getType(10, 'strict') = 'integer' * getType('true', 'strict') = 'string' * getType(true, 'strict') = 'boolean' * * // value - value to check * // { any = false } strict - if truthy, also checks JavaScript tyoe * // { SchemaType } */ function getType(value, strict) { if (strict === void 0) { strict = false; } if (!isDefined(value)) { return 'null'; } if (isArray(value)) { return 'array'; } if (isObject(value)) { return 'object'; } if (isBoolean(value, 'strict')) { return 'boolean'; } if (isInteger(value, strict)) { return 'integer'; } if (isNumber(value, strict)) { return 'number'; } if (isString(value) || (!strict && isDate(value))) { return 'string'; } return null; } /** * 'isType' function * * Checks wether an input (probably string) value contains data of * a specified JSON Schema type * * // { PrimitiveValue } value - value to check * // { SchemaPrimitiveType } type - type to check * // { boolean } */ function isType(value, type) { switch (type) { case 'string': return isString(value) || isDate(value); case 'number': return isNumber(value); case 'integer': return isInteger(value); case 'boolean': return isBoolean(value); case 'null': return !hasValue(value); default: console.error("isType error: \"" + type + "\" is not a recognized type."); return null; } } /** * 'isPrimitive' function * * Checks wether an input value is a JavaScript primitive type: * string, number, boolean, or null. * * // value - value to check * // { boolean } */ function isPrimitive(value) { return (isString(value) || isNumber(value) || isBoolean(value, 'strict') || value === null); } /** * 'toJavaScriptType' function * * Converts an input (probably string) value to a JavaScript primitive type - * 'string', 'number', 'boolean', or 'null' - before storing in a JSON object. * * Does not coerce values (other than null), and only converts the types * of values that would otherwise be valid. * * If the optional third parameter 'strictIntegers' is TRUE, and the * JSON Schema type 'integer' is specified, it also verifies the input value * is an integer and, if it is, returns it as a JaveScript number. * If 'strictIntegers' is FALSE (or not set) the type 'integer' is treated * exactly the same as 'number', and allows decimals. * * Valid Examples: * toJavaScriptType('10', 'number' ) = 10 // '10' is a number * toJavaScriptType('10', 'integer') = 10 // '10' is also an integer * toJavaScriptType( 10, 'integer') = 10 // 10 is still an integer * toJavaScriptType( 10, 'string' ) = '10' // 10 can be made into a string * toJavaScriptType('10.5', 'number' ) = 10.5 // '10.5' is a number * * Invalid Examples: * toJavaScriptType('10.5', 'integer') = null // '10.5' is not an integer * toJavaScriptType( 10.5, 'integer') = null // 10.5 is still not an integer * * // { PrimitiveValue } value - value to convert * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - types to convert to * // { boolean = false } strictIntegers - if FALSE, treat integers as numbers * // { PrimitiveValue } */ function toJavaScriptType(value, types, strictIntegers) { if (strictIntegers === void 0) { strictIntegers = true; } if (!isDefined(value)) { return null; } if (isString(types)) { types = [types]; } if (strictIntegers && inArray('integer', types)) { if (isInteger(value, 'strict')) { return value; } if (isInteger(value)) { return parseInt(value, 10); } } if (inArray('number', types) || (!strictIntegers && inArray('integer', types))) { if (isNumber(value, 'strict')) { return value; } if (isNumber(value)) { return parseFloat(value); } } if (inArray('string', types)) { if (isString(value)) { return value; } // If value is a date, and types includes 'string', // convert the date to a string if (isDate(value)) { return value.toISOString().slice(0, 10); } if (isNumber(value)) { return value.toString(); } } // If value is a date, and types includes 'integer' or 'number', // but not 'string', convert the date to a number if (isDate(value) && (inArray('integer', types) || inArray('number', types))) { return value.getTime(); } if (inArray('boolean', types)) { if (isBoolean(value, true)) { return true; } if (isBoolean(value, false)) { return false; } } return null; } /** * 'toSchemaType' function * * Converts an input (probably string) value to the "best" JavaScript * equivalent available from an allowed list of JSON Schema types, which may * contain 'string', 'number', 'integer', 'boolean', and/or 'null'. * If necssary, it does progressively agressive type coersion. * It will not return null unless null is in the list of allowed types. * * Number conversion examples: * toSchemaType('10', ['number','integer','string']) = 10 // integer * toSchemaType('10', ['number','string']) = 10 // number * toSchemaType('10', ['string']) = '10' // string * toSchemaType('10.5', ['number','integer','string']) = 10.5 // number * toSchemaType('10.5', ['integer','string']) = '10.5' // string * toSchemaType('10.5', ['integer']) = 10 // integer * toSchemaType(10.5, ['null','boolean','string']) = '10.5' // string * toSchemaType(10.5, ['null','boolean']) = true // boolean * * String conversion examples: * toSchemaType('1.5x', ['boolean','number','integer','string']) = '1.5x' // string * toSchemaType('1.5x', ['boolean','number','integer']) = '1.5' // number * toSchemaType('1.5x', ['boolean','integer']) = '1' // integer * toSchemaType('1.5x', ['boolean']) = true // boolean * toSchemaType('xyz', ['number','integer','boolean','null']) = true // boolean * toSchemaType('xyz', ['number','integer','null']) = null // null * toSchemaType('xyz', ['number','integer']) = 0 // number * * Boolean conversion examples: * toSchemaType('1', ['integer','number','string','boolean']) = 1 // integer * toSchemaType('1', ['number','string','boolean']) = 1 // number * toSchemaType('1', ['string','boolean']) = '1' // string * toSchemaType('1', ['boolean']) = true // boolean * toSchemaType('true', ['number','string','boolean']) = 'true' // string * toSchemaType('true', ['boolean']) = true // boolean * toSchemaType('true', ['number']) = 0 // number * toSchemaType(true, ['number','string','boolean']) = true // boolean * toSchemaType(true, ['number','string']) = 'true' // string * toSchemaType(true, ['number']) = 1 // number * * // { PrimitiveValue } value - value to convert * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - allowed types to convert to * // { PrimitiveValue } */ function toSchemaType(value, types) { if (!isArray(types)) { types = [types]; } if (types.includes('null') && !hasValue(value)) { return null; } if (types.includes('boolean') && !isBoolean(value, 'strict')) { return value; } if (types.includes('integer')) { var testValue = toJavaScriptType(value, 'integer'); if (testValue !== null) { return +testValue; } } if (types.includes('number')) { var testValue = toJavaScriptType(value, 'number'); if (testValue !== null) { return +testValue; } } if ((isString(value) || isNumber(value, 'strict')) && types.includes('string')) { // Convert number to string return toJavaScriptType(value, 'string'); } if (types.includes('boolean') && isBoolean(value)) { return toJavaScriptType(value, 'boolean'); } if (types.includes('string')) { // Convert null & boolean to string if (value === null) { return ''; } var testValue = toJavaScriptType(value, 'string'); if (testValue !== null) { return testValue; } } if ((types.includes('number') || types.includes('integer'))) { if (value === true) { return 1; } // Convert boolean & null to number if (value === false || value === null || value === '') { return 0; } } if (types.includes('number')) { // Convert mixed string to number var testValue = parseFloat(value); if (!!testValue) { return testValue; } } if (types.includes('integer')) { // Convert string or number to integer var testValue = parseInt(value, 10); if (!!testValue) { return testValue; } } if (types.includes('boolean')) { // Convert anything to boolean return !!value; } if ((types.includes('number') || types.includes('integer')) && !types.includes('null')) { return 0; // If null not allowed, return 0 for non-convertable values } } /** * 'isPromise' function * * // object * // { boolean } */ function isPromise(object) { return !!object && typeof object.then === 'function'; } /** * 'isObservable' function * * // object * // { boolean } */ function isObservable(object) { return !!object && typeof object.subscribe === 'function'; } /** * '_toPromise' function * * // { object } object * // { Promise<any> } */ function _toPromise(object) { return isPromise(object) ? object : object.toPromise(); } /** * 'toObservable' function * * // { object } object * // { Observable<any> } */ function toObservable(object) { var observable = isPromise(object) ? from(object) : object; if (isObservable(observable)) { return observable; } console.error('toObservable error: Expected validator to return Promise or Observable.'); return new Observable(); } /** * 'inArray' function * * Searches an array for an item, or one of a list of items, and returns true * as soon as a match is found, or false if no match. * * If the optional third parameter allIn is set to TRUE, and the item to find * is an array, then the function returns true only if all elements from item * are found in the array list, and false if any element is not found. If the * item to find is not an array, setting allIn to TRUE has no effect. * * // { any|any[] } item - the item to search for * // array - the array to search * // { boolean = false } allIn - if TRUE, all items must be in array * // { boolean } - true if item(s) in array, false otherwise */ function inArray(item, array, allIn) { if (allIn === void 0) { allIn = false; } if (!isDefined(item) || !isArray(array)) { return false; } return isArray(item) ? item[allIn ? 'every' : 'some'](function (subItem) { return array.includes(subItem); }) : array.includes(item); } /** * 'xor' utility function - exclusive or * * Returns true if exactly one of two values is truthy. * * // value1 - first value to check * // value2 - second value to check * // { boolean } - true if exactly one input value is truthy, false if not */ function xor(value1, value2) { return (!!value1 && !value2) || (!value1 && !!value2); } /** * Utility function library: * * addClasses, copy, forEach, forEachCopy, hasOwn, mergeFilteredObject, * uniqueItems, commonItems, fixTitle, toTitleCase */ /** * 'addClasses' function * * Merges two space-delimited lists of CSS classes and removes duplicates. * * // {string | string[] | Set<string>} oldClasses * // {string | string[] | Set<string>} newClasses * // {string | string[] | Set<string>} - Combined classes */ function addClasses(oldClasses, newClasses) { var badType = function (i) { return !isSet(i) && !isArray(i) && !isString(i); }; if (badType(newClasses)) { return oldClasses; } if (badType(oldClasses)) { oldClasses = ''; } var toSet = function (i) { return isSet(i) ? i : isArray(i) ? new Set(i) : new Set(i.split(' ')); }; var combinedSet = toSet(oldClasses); var newSet = toSet(newClasses); newSet.forEach(function (c) { return combinedSet.add(c); }); if (isSet(oldClasses)) { return combinedSet; } if (isArray(oldClasses)) { return Array.from(combinedSet); } return Array.from(combinedSet).join(' '); } /** * 'copy' function * * Makes a shallow copy of a JavaScript object, array, Map, or Set. * If passed a JavaScript primitive value (string, number, boolean, or null), * it returns the value. * * // {Object|Array|string|number|boolean|null} object - The object to copy * // {boolean = false} errors - Show errors? * // {Object|Array|string|number|boolean|null} - The copied object */ function copy(object, errors) { if (errors === void 0) { errors = false; } if (typeof object !== 'object' || object === null) { return object; } if (isMap(object)) { return new Map(object); } if (isSet(object)) { return new Set(object); } if (isArray(object)) { return __spread(object); } if (isObject(object)) { return __assign({}, object); } if (errors) { console.error('copy error: Object to copy must be a JavaScript object or value.'); } return object; } /** * 'forEach' function * * Iterates over all items in the first level of an object or array * and calls an iterator funciton on each item. * * The iterator function is called with four values: * 1. The current item's value * 2. The current item's key * 3. The parent object, which contains the current item * 4. The root object * * Setting the optional third parameter to 'top-down' or 'bottom-up' will cause * it to also recursively iterate over items in sub-objects or sub-arrays in the * specified direction. * * // {Object|Array} object - The object or array to iterate over * // {function} fn - the iterator funciton to call on each item * // {boolean = false} errors - Show errors? * // {void} */ function forEach(object, fn, recurse, rootObject, errors) { var e_1, _a; if (recurse === void 0) { recurse = false; } if (rootObject === void 0) { rootObject = object; } if (errors === void 0) { errors = false; } if (isEmpty(object)) { return; } if ((isObject(object) || isArray(object)) && typeof fn === 'function') { try { for (var _b = __values(Object.keys(object)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; var value = object[key]; if (recurse === 'bottom-up' && (isObject(value) || isArray(value))) { forEach(value, fn, recurse, rootObject); } fn(value, key, object, rootObject); if (recurse === 'top-down' && (isObject(value) || isArray(value))) { forEach(value, fn, recurse, rootObject); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } } if (errors) { if (typeof fn !== 'function') { console.error('forEach error: Iterator must be a function.'); console.error('function', fn); } if (!isObject(object) && !isArray(object)) { console.error('forEach error: Input object must be an object or array.'); console.error('object', object); } } } /** * 'forEachCopy' function * * Iterates over all items in the first level of an object or array * and calls an iterator function on each item. Returns a new object or array * with the same keys or indexes as the original, and values set to the results * of the iterator function. * * Does NOT recursively iterate over items in sub-objects or sub-arrays. * * // {Object | Array} object - The object or array to iterate over * // {function} fn - The iterator funciton to call on each item * // {boolean = false} errors - Show errors? * // {Object | Array} - The resulting object or array */ function forEachCopy(object, fn, errors) { var e_2, _a; if (errors === void 0) { errors = false; } if (!hasValue(object)) { return; } if ((isObject(object) || isArray(object)) && typeof object !== 'function') { var newObject = isArray(object) ? [] : {}; try { for (var _b = __values(Object.keys(object)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; newObject[key] = fn(object[key], key, object); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } return newObject; } if (errors) { if (typeof fn !== 'function') { console.error('forEachCopy error: Iterator must be a function.'); console.error('function', fn); } if (!isObject(object) && !isArray(object)) { console.error('forEachCopy error: Input object must be an object or array.'); console.error('object', object); } } } /** * 'hasOwn' utility function * * Checks whether an object or array has a particular property. * * // {any} object - the object to check * // {string} property - the property to look for * // {boolean} - true if object has property, false if not */ function hasOwn(object, property) { if (!object || !['number', 'string', 'symbol'].includes(typeof property) || (!isObject(object) && !isArray(object) && !isMap(object) && !isSet(object))) { return false; } if (isMap(object) || isSet(object)) { return object.has(property); } if (typeof property === 'number') { if (isArray(object)) { return object[property]; } property = property + ''; } return object.hasOwnProperty(property); } /** * Types of possible expressions which the app is able to evaluate. */ var ExpressionType; (function (ExpressionType) { ExpressionType[ExpressionType["EQUALS"] = 0] = "EQUALS"; ExpressionType[ExpressionType["NOT_EQUALS"] = 1] = "NOT_EQUALS"; ExpressionType[ExpressionType["NOT_AN_EXPRESSION"] = 2] = "NOT_AN_EXPRESSION"; })(ExpressionType || (ExpressionType = {})); /** * Detects the type of expression from the given candidate. `==` for equals, * `!=` for not equals. If none of these are contained in the candidate, the candidate * is not considered to be an expression at all and thus `NOT_AN_EXPRESSION` is returned. * // {expressionCandidate} expressionCandidate - potential expression */ function getExpressionType(expressionCandidate) { if (expressionCandidate.indexOf('==') !== -1) { return ExpressionType.EQUALS; } if (expressionCandidate.toString().indexOf('!=') !== -1) { return ExpressionType.NOT_EQUALS; } return ExpressionType.NOT_AN_EXPRESSION; } function isEqual(expressionType) { return expressionType === ExpressionType.EQUALS; } function isNotEqual(expressionType) { return expressionType === ExpressionType.NOT_EQUALS; } function isNotExpression(expressionType) { return expressionType === ExpressionType.NOT_AN_EXPRESSION; } /** * Splits the expression key by the expressionType on a pair of values * before and after the equals or nor equals sign. * // {expressionType} enum of an expression type * // {key} the given key from a for loop iver all conditions */ function getKeyAndValueByExpressionType(expressionType, key) { if (isEqual(expressionType)) { return key.split('==', 2); } if (isNotEqual(expressionType)) { return key.split('!=', 2); } return null; } function cleanValueOfQuotes(keyAndValue) { if (keyAndValue.charAt(0) === '\'' && keyAndValue.charAt(keyAndValue.length - 1) === '\'') { return keyAndValue.replace('\'', '').replace('\'', ''); } return keyAndValue; } /** * 'mergeFilteredObject' utility function * * Shallowly merges two objects, setting key and values from source object * in target object, excluding specified keys. * * Optionally, it can also use functions to transform the key names and/or * the values of the merging object. * * // {PlainObject} targetObject - Target object to add keys and values to * // {PlainObject} sourceObject - Source object to copy keys and values from * // {string[]} excludeKeys - Array of keys to exclude * // {(string: string) => string = (k) => k} keyFn - Function to apply to keys * // {(any: any) => any = (v) => v} valueFn - Function to apply to values * // {PlainObject} - Returns targetObject */ function mergeFilteredObject(targetObject, sourceObject, excludeKeys, keyFn, valFn) { var e_3, _a; if (excludeKeys === void 0) { excludeKeys = []; } if (keyFn === void 0) { keyFn = function (key) { return key; }; } if (valFn === void 0) { valFn = function (val) { return val; }; } if (!isObject(sourceObject)) { return targetObject; } if (!isObject(targetObject)) { targetObject = {}; } try { for (var _b = __values(Object.keys(sourceObject)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; if (!inArray(key, excludeKeys) && isDefined(sourceObject[key])) { targetObject[keyFn(key)] = valFn(sourceObject[key]); } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_3) throw e_3.error; } } return targetObject; } /** * 'uniqueItems' function * * Accepts any number of string value inputs, * and returns an array of all input vaues, excluding duplicates. * * // {...string} ...items - * // {string[]} - */ function uniqueItems() { var e_4, _a; var items = []; for (var _i = 0; _i < arguments.length; _i++) { items[_i] = arguments[_i]; } var returnItems = []; try { for (var items_1 = __values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) { var item = items_1_1.value; if (!returnItems.includes(item)) { returnItems.push(item); } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1); } finally { if (e_4) throw e_4.error; } } return returnItems; } /** * 'commonItems' function * * Accepts any number of strings or arrays of string values, * and returns a single array containing only values present in all inputs. * * // {...string|string[]} ...arrays - * // {string[]} - */ function commonItems() { var e_5, _a; var arrays = []; for (var _i = 0; _i < arguments.length; _i++) { arrays[_i] = arguments[_i]; } var returnItems = null; var _loop_1 = function (array) { if (isString(array)) { array = [array]; } returnItems = returnItems === null ? __spread(array) : returnItems.filter(function (item) { return array.includes(item); }); if (!returnItems.length) { return { value: [] }; } }; try { for (var arrays_1 = __values(arrays), arrays_1_1 = arrays_1.next(); !arrays_1_1.done; arrays_1_1 = arrays_1.next()) { var array = arrays_1_1.value; var state_1 = _loop_1(array); if (typeof state_1 === "object") return state_1.value; } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (arrays_1_1 && !arrays_1_1.done && (_a = arrays_1.return)) _a.call(arrays_1); } finally { if (e_5) throw e_5.error; } } return returnItems; } /** * 'fixTitle' function * * * // {string} input - * // {string} - */ function fixTitle(name) { return name && toTitleCase(name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/_/g, ' ')); } /** * 'toTitleCase' function * * Intelligently converts an input string to Title Case. * * Accepts an optional second parameter with a list of additional * words and abbreviations to force into a particular case. * * This function is built on prior work by John Gruber and David Gouch: * http://daringfireball.net/2008/08/title_case_update * https://github.com/gouch/to-title-case * * // {string} input - * // {string|string[]} forceWords? - * // {string} - */ function toTitleCase(input, forceWords) { if (!isString(input)) { return input; } var forceArray = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'en', 'for', 'if', 'in', 'nor', 'of', 'on', 'or', 'per', 'the', 'to', 'v', 'v.', 'vs', 'vs.', 'via']; if (isString(forceWords)) { forceWords = forceWords.split('|'); } if (isArray(forceWords)) { forceArray = forceArray.concat(forceWords); } var forceArrayLower = forceArray.map(function (w) { return w.toLowerCase(); }); var noInitialCase = input === input.toUpperCase() || input === input.toLowerCase(); var prevLastChar = ''; input = input.trim(); return input.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function (word, idx) { if (!noInitialCase && word.slice(1).search(/[A-Z]|\../) !== -1) { return word; } else { var newWord = void 0; var forceWord = forceArray[forceArrayLower.indexOf(word.toLowerCase())]; if (!forceWord) { if (noInitialCase) { if (word.slice(1).search(/\../) !== -1) { newWord = word.toLowerCase(); } else { newWord = word[0].toUpperCase() + word.slice(1).toLowerCase(); } } else { newWord = word[0].toUpperCase() + word.slice(1); } } else if (forceWord === forceWord.toLowerCase() && (idx === 0 || idx + word.length === input.length || prevLastChar === ':' || input[idx - 1].search(/[^\s-]/) !== -1 || (input[idx - 1] !== '-' && input[idx + word.length] === '-'))) { newWord = forceWord[0].toUpperCase() + forceWord.slice(1); } else { newWord = forceWord; } prevLastChar = word.slice(-1); return newWord; } }); } var JsonPointer = /** @class */ (function () { function JsonPointer() { } JsonPointer_1 = JsonPointer; /** * 'get' function * * Uses a JSON Pointer to retrieve a value from an object. * * // { object } object - Object to get value from * // { Pointer } pointer - JSON Pointer (string or array) * // { number = 0 } startSlice - Zero-based index of first Pointer key to use * // { number } endSlice - Zero-based index of last Pointer key to use * // { boolean = false } getBoolean - Return only true or false? * // { boolean = false } errors - Show error if not found? * // { object } - Located value (or true or false if getBoolean = true) */ JsonPointer.get = function (object, pointer, startSlice, endSlice, getBoolean, errors) { var e_1, _a; if (startSlice === void 0) { startSlice = 0; } if (endSlice === void 0) { endSlice = null; } if (getBoolean === void 0) { getBoolean = false; } if (errors === void 0) { errors = false; } if (object === null) { return getBoolean ? false : undefined; } var keyArray = this.parse(pointer, errors); if (typeof object === 'object' && keyArray !== null) { var subObject = object; if (startSlice >= keyArray.length || endSlice <= -keyArray.length) { return object; } if (startSlice <= -keyArray.length) { startSlice = 0; } if (!isDefined(endSlice) || endSlice >= keyArray.length) { endSlice = keyArray.length; } keyArray = keyArray.slice(startSlice, endSlice); try { for (var keyArray_1 = __values(keyArray), keyArray_1_1 = keyArray_1.next(); !keyArray_1_1.done; keyArray_1_1 = keyArray_1.next()) { var key = keyArray_1_1.value; if (key === '-' && isArray(subObject) && subObject.length) { key = subObject.length - 1; } if (isMap(subObject) && subObject.has(key)) { subObject = subObject.get(key); } else if (typeof subObject === 'object' && subObject !== null && hasOwn(subObject, key)) { subObject = subObject[key]; } else { var evaluatedExpression = JsonPointer_1.evaluateExpression(subObject, key); if (evaluatedExpression.passed) { subObject = evaluatedExpression.key ? subObject[evaluatedExpression.key] : subObject; } else { this.logErrors(errors, key, pointer, object); return getBoolean ? false : undefined; } } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (keyArray_1_1 && !keyArray_1_1.done && (_a = keyArray_1.return)) _a.call(keyArray_1); } finally { if (e_1) throw e_1.error; } } return getBoolean ? true : subObject; } if (errors && keyArray === null) { console.error("get error: Invalid JSON Pointer: " + pointer); } if (errors && typeof object !== 'object') { console.error('get error: Invalid object:'); console.error(object); } return getBoolean ? false : undefined; }; JsonPointer.logErrors = function (errors, key, pointer, object) { if (errors) { console.error("get error: \"" + key + "\" key not found in object."); console.error(pointer); console.error(object); } }; /** * Evaluates conditional expression in form of `model.<property>==<value>` or * `model.<property>!=<value>` where the first one means that the value must match to be * shown in a form, while the former shows the property only when the property value is not * set, or does not equal the given value. * * // { subObject } subObject - an object containing the data values of properties * // { key } key - the key from the for loop in a form of `<property>==<value>` * * Returns the object with two properties. The property passed informs whether * the expression evaluated successfully and the property key returns either the same * key if it is not contained inside the subObject or the key of the property if it is contained. */ JsonPointer.evaluateExpression = function (subObject, key) { var defaultResult = { passed: false, key: key }; var keysAndExpression = this.parseKeysAndExpression(key, subObject); if (!keysAndExpression) { return defaultResult; } var ownCheckResult = this.doOwnCheckResult(subObject, keysAndExpression); if (ownCheckResult) { return ownCheckResult; } var cleanedValue = cleanValueOfQuotes(keysAndExpression.keyAndValue[1]); var evaluatedResult = this.performExpressionOnValue(keysAndExpression, cleanedValue, subObject); if (evaluatedResult) { return evaluatedResult; } return defaultResult; }; /** * Performs the actual evaluation on the given expression with given values and keys. * // { cleanedValue } cleanedValue - the given valued cleaned of quotes if it had any * // { subObject } subObject - the object with properties values * // { keysAndExpression } keysAndExpression - an object holding the expressions with */ JsonPointer.performExpressionOnValue = function (keysAndExpression, cleanedValue, subObject) { var propertyByKey = subObject[keysAndExpression.keyAndValue[0]]; if (this.doComparisonByExpressionType(keysAndExpression.expressionType, propertyByKey, cleanedValue)) { return { passed: true, key: keysAndExpression.keyAndValue[0] }; } return null; }; JsonPointer.doComparisonByExpressionType = function (expressionType, propertyByKey, cleanedValue) { if (isEqual(expressionType)) { return propertyByKey === cleanedValue; } if (isNotEqual(expressionType)) { return propertyByKey !== cleanedValue; } return false; }; /** * Does the checks when the parsed key is actually no a property inside subObject. * That would mean that the equal comparison makes no sense and thus the negative result * is returned, and the not equal comparison is not necessary because it doesn't equal * obviously. Returns null when the given key is a real property inside the subObject. * // { subObject } subObject - the object with properties values * // { keysAndExpression } keysAndExpression - an object holding the expressions with * the associated keys. */ JsonPointer.doOwnCheckResult = function (subObject, keysAndExpression) { var ownCheckResult = null; if (!hasOwn(subObject, keysAndExpression.keyAndValue[0])) { if (isEqual(keysAndExpression.expressionType)) { ownCheckResult = { passed: false, key: null }; } if (isNotEqual(keysAndExpression.expressionType)) { ownCheckResult = { passed: true, key: null }; } } return ownCheckResult; }; /** * Does the basic checks and tries to parse an expression and a pair * of key and value. * // { key } key - the original for loop created value containing key and value in one string * // { subObject } subObject - the object with properties values */ JsonPointer.parseKeysAndExpression = function (key, subObject) { if (this.keyOrSubObjEmpty(key, subObject)) { return null; } var expressionType = getExpressionType(key.toString()); if (isNotExpression(expressionType)) { return null; } var keyAndValue = getKeyAndValueByExpressionType(expressionType, key); if (!keyAndValue || !keyAndValue[0] || !keyAndValue[1]) { return null; } return { expressionType: expressionType, keyAndValue: keyAndValue }; }; JsonPointer.keyOrSubObjEmpty = function (key, subObject) { return !key || !subObject; }; /** * 'getCopy' function * * Uses a JSON Pointer to deeply clone a value from an object. * * // { object } object - Object to get value from * // { Pointer } pointer - JSON Pointer (string or array) * // { number = 0 } startSlice - Zero-based index of first Pointer key to use * // { number } endSlice - Zero-based index of last Pointer key to use * // { boolean = false } getBoolean - Return only true or false? * // { boolean = false } errors - Show error if not found? * // { object } - Located value (or true or false if getBoolean = true) */ JsonPointer.getCopy = function (object, pointer, startSlice, endSlice, getBoolean, errors) { if (startSlice === void 0) { startSlice = 0; } if (endSlice === void 0) { endSlice = null; } if (getBoolean === void 0) { getBoolean = false; } if (errors === void 0) { errors = false; } var objectToCopy = this.get(object, pointer, startSlice, endSlice, getBoolean, errors); return this.forEachDeepCopy(objectToCopy); }; /** * 'getFirst' function * * Takes an array of JSON Pointers and objects, * checks each object for a value specified by the pointer, * and returns the first value found. * * // { [object, pointer][] } items - Array of objects and pointers to check * // { any = null } defaultValue - Value to return if nothing found * // { boolean = false } getCopy - Return a copy instead? * // - First value found */ JsonPointer.getFirst = function (items, defaultValue, getCopy) { var e_2, _a, e_3, _b; if (defaultValue === void 0) { defaultValue = null; } if (getCopy === void 0) { getCopy = false; } if (isEmpty(items)) { return; } if (isArray(items)) { try { for (var items_1 = __values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) { var item = items_1_1.value; if (isEmpty(item)) { continue; } if (isArray(item) && item.length >= 2) { if (isEmpty(item[0]) || isEmpty(item[1])) { continue; } var value = getCopy ? this.getCopy(item[0], item[1]) : this.get(item[0], item[1]); if (value) { return value; } continue; } console.error('getFirst error: Input not in correct format.\n' + 'Should be: [ [ object1, pointer1 ], [ obje