angular6-json-schema-form
Version:
Angular JSON Schema Form builder
1,341 lines (1,338 loc) • 560 kB
JavaScript
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