@ajsf/core
Version:
Angular JSON Schema Form builder core
1 lines • 683 kB
Source Map (JSON)
{"version":3,"file":"ajsf-core.mjs","sources":["../../../../projects/ajsf-core/src/lib/shared/convert-schema-to-draft6.function.ts","../../../../projects/ajsf-core/src/lib/shared/validator.functions.ts","../../../../projects/ajsf-core/src/lib/shared/utility.functions.ts","../../../../projects/ajsf-core/src/lib/shared/jsonpointer.functions.ts","../../../../projects/ajsf-core/src/lib/shared/format-regex.constants.ts","../../../../projects/ajsf-core/src/lib/shared/json.validators.ts","../../../../projects/ajsf-core/src/lib/shared/merge-schemas.function.ts","../../../../projects/ajsf-core/src/lib/shared/json-schema.functions.ts","../../../../projects/ajsf-core/src/lib/shared/form-group.functions.ts","../../../../projects/ajsf-core/src/lib/shared/layout.functions.ts","../../../../projects/ajsf-core/src/lib/shared/index.ts","../../../../projects/ajsf-core/src/lib/locale/de-validation-messages.ts","../../../../projects/ajsf-core/src/lib/locale/en-validation-messages.ts","../../../../projects/ajsf-core/src/lib/locale/es-validation-messages.ts","../../../../projects/ajsf-core/src/lib/locale/fr-validation-messages.ts","../../../../projects/ajsf-core/src/lib/locale/it-validation-messages.ts","../../../../projects/ajsf-core/src/lib/locale/pt-validation-messages.ts","../../../../projects/ajsf-core/src/lib/locale/zh-validation-messages.ts","../../../../projects/ajsf-core/src/lib/json-schema-form.service.ts","../../../../projects/ajsf-core/src/lib/framework-library/framework.ts","../../../../projects/ajsf-core/src/lib/widget-library/add-reference.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/button.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/checkbox.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/checkboxes.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/file.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/input.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/message.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/none.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/number.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/one-of.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/radios.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/select-framework.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/orderable.directive.ts","../../../../projects/ajsf-core/src/lib/widget-library/root.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/section.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/select.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/select-widget.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/submit.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/tabs.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/template.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/textarea.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/widget-library.service.ts","../../../../projects/ajsf-core/src/lib/framework-library/framework-library.service.ts","../../../../projects/ajsf-core/src/lib/json-schema-form.component.ts","../../../../projects/ajsf-core/src/lib/json-schema-form.component.html","../../../../projects/ajsf-core/src/lib/framework-library/no-framework.component.ts","../../../../projects/ajsf-core/src/lib/framework-library/no-framework.component.html","../../../../projects/ajsf-core/src/lib/framework-library/no.framework.ts","../../../../projects/ajsf-core/src/lib/widget-library/hidden.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/tab.component.ts","../../../../projects/ajsf-core/src/lib/widget-library/index.ts","../../../../projects/ajsf-core/src/lib/widget-library/widget-library.module.ts","../../../../projects/ajsf-core/src/lib/framework-library/no-framework.module.ts","../../../../projects/ajsf-core/src/lib/json-schema-form.module.ts","../../../../projects/ajsf-core/src/public_api.ts","../../../../projects/ajsf-core/src/ajsf-core.ts"],"sourcesContent":["import cloneDeep from 'lodash/cloneDeep';\n\n/**\n * 'convertSchemaToDraft6' function\n *\n * Converts a JSON Schema from draft 1 through 4 format to draft 6 format\n *\n * Inspired by on geraintluff's JSON Schema 3 to 4 compatibility function:\n * https://github.com/geraintluff/json-schema-compatibility\n * Also uses suggestions from AJV's JSON Schema 4 to 6 migration guide:\n * https://github.com/epoberezkin/ajv/releases/tag/5.0.0\n * And additional details from the official JSON Schema documentation:\n * http://json-schema.org\n *\n * // { object } originalSchema - JSON schema (draft 1, 2, 3, 4, or 6)\n * // { OptionObject = {} } options - options: parent schema changed?, schema draft number?\n * // { object } - JSON schema (draft 6)\n */\nexport interface OptionObject { changed?: boolean; draft?: number; }\nexport function convertSchemaToDraft6(schema, options: OptionObject = {}) {\n let draft: number = options.draft || null;\n let changed: boolean = options.changed || false;\n\n if (typeof schema !== 'object') { return schema; }\n if (typeof schema.map === 'function') {\n return [...schema.map(subSchema => convertSchemaToDraft6(subSchema, { changed, draft }))];\n }\n let newSchema = { ...schema };\n const simpleTypes = ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'];\n\n if (typeof newSchema.$schema === 'string' &&\n /http\\:\\/\\/json\\-schema\\.org\\/draft\\-0\\d\\/schema\\#/.test(newSchema.$schema)\n ) {\n draft = newSchema.$schema[30];\n }\n\n // Convert v1-v2 'contentEncoding' to 'media.binaryEncoding'\n // Note: This is only used in JSON hyper-schema (not regular JSON schema)\n if (newSchema.contentEncoding) {\n newSchema.media = { binaryEncoding: newSchema.contentEncoding };\n delete newSchema.contentEncoding;\n changed = true;\n }\n\n // Convert v1-v3 'extends' to 'allOf'\n if (typeof newSchema.extends === 'object') {\n newSchema.allOf = typeof newSchema.extends.map === 'function' ?\n newSchema.extends.map(subSchema => convertSchemaToDraft6(subSchema, { changed, draft })) :\n [convertSchemaToDraft6(newSchema.extends, { changed, draft })];\n delete newSchema.extends;\n changed = true;\n }\n\n // Convert v1-v3 'disallow' to 'not'\n if (newSchema.disallow) {\n if (typeof newSchema.disallow === 'string') {\n newSchema.not = { type: newSchema.disallow };\n } else if (typeof newSchema.disallow.map === 'function') {\n newSchema.not = {\n anyOf: newSchema.disallow\n .map(type => typeof type === 'object' ? type : { type })\n };\n }\n delete newSchema.disallow;\n changed = true;\n }\n\n // Convert v3 string 'dependencies' properties to arrays\n if (typeof newSchema.dependencies === 'object' &&\n Object.keys(newSchema.dependencies)\n .some(key => typeof newSchema.dependencies[key] === 'string')\n ) {\n newSchema.dependencies = { ...newSchema.dependencies };\n Object.keys(newSchema.dependencies)\n .filter(key => typeof newSchema.dependencies[key] === 'string')\n .forEach(key => newSchema.dependencies[key] = [newSchema.dependencies[key]]);\n changed = true;\n }\n\n // Convert v1 'maxDecimal' to 'multipleOf'\n if (typeof newSchema.maxDecimal === 'number') {\n newSchema.multipleOf = 1 / Math.pow(10, newSchema.maxDecimal);\n delete newSchema.divisibleBy;\n changed = true;\n if (!draft || draft === 2) { draft = 1; }\n }\n\n // Convert v2-v3 'divisibleBy' to 'multipleOf'\n if (typeof newSchema.divisibleBy === 'number') {\n newSchema.multipleOf = newSchema.divisibleBy;\n delete newSchema.divisibleBy;\n changed = true;\n }\n\n // Convert v1-v2 boolean 'minimumCanEqual' to 'exclusiveMinimum'\n if (typeof newSchema.minimum === 'number' && newSchema.minimumCanEqual === false) {\n newSchema.exclusiveMinimum = newSchema.minimum;\n delete newSchema.minimum;\n changed = true;\n if (!draft) { draft = 2; }\n } else if (typeof newSchema.minimumCanEqual === 'boolean') {\n delete newSchema.minimumCanEqual;\n changed = true;\n if (!draft) { draft = 2; }\n }\n\n // Convert v3-v4 boolean 'exclusiveMinimum' to numeric\n if (typeof newSchema.minimum === 'number' && newSchema.exclusiveMinimum === true) {\n newSchema.exclusiveMinimum = newSchema.minimum;\n delete newSchema.minimum;\n changed = true;\n } else if (typeof newSchema.exclusiveMinimum === 'boolean') {\n delete newSchema.exclusiveMinimum;\n changed = true;\n }\n\n // Convert v1-v2 boolean 'maximumCanEqual' to 'exclusiveMaximum'\n if (typeof newSchema.maximum === 'number' && newSchema.maximumCanEqual === false) {\n newSchema.exclusiveMaximum = newSchema.maximum;\n delete newSchema.maximum;\n changed = true;\n if (!draft) { draft = 2; }\n } else if (typeof newSchema.maximumCanEqual === 'boolean') {\n delete newSchema.maximumCanEqual;\n changed = true;\n if (!draft) { draft = 2; }\n }\n\n // Convert v3-v4 boolean 'exclusiveMaximum' to numeric\n if (typeof newSchema.maximum === 'number' && newSchema.exclusiveMaximum === true) {\n newSchema.exclusiveMaximum = newSchema.maximum;\n delete newSchema.maximum;\n changed = true;\n } else if (typeof newSchema.exclusiveMaximum === 'boolean') {\n delete newSchema.exclusiveMaximum;\n changed = true;\n }\n\n // Search object 'properties' for 'optional', 'required', and 'requires' items,\n // and convert them into object 'required' arrays and 'dependencies' objects\n if (typeof newSchema.properties === 'object') {\n const properties = { ...newSchema.properties };\n const requiredKeys = Array.isArray(newSchema.required) ?\n new Set(newSchema.required) : new Set();\n\n // Convert v1-v2 boolean 'optional' properties to 'required' array\n if (draft === 1 || draft === 2 ||\n Object.keys(properties).some(key => properties[key].optional === true)\n ) {\n Object.keys(properties)\n .filter(key => properties[key].optional !== true)\n .forEach(key => requiredKeys.add(key));\n changed = true;\n if (!draft) { draft = 2; }\n }\n\n // Convert v3 boolean 'required' properties to 'required' array\n if (Object.keys(properties).some(key => properties[key].required === true)) {\n Object.keys(properties)\n .filter(key => properties[key].required === true)\n .forEach(key => requiredKeys.add(key));\n changed = true;\n }\n\n if (requiredKeys.size) { newSchema.required = Array.from(requiredKeys); }\n\n // Convert v1-v2 array or string 'requires' properties to 'dependencies' object\n if (Object.keys(properties).some(key => properties[key].requires)) {\n const dependencies = typeof newSchema.dependencies === 'object' ?\n { ...newSchema.dependencies } : {};\n Object.keys(properties)\n .filter(key => properties[key].requires)\n .forEach(key => dependencies[key] =\n typeof properties[key].requires === 'string' ?\n [properties[key].requires] : properties[key].requires\n );\n newSchema.dependencies = dependencies;\n changed = true;\n if (!draft) { draft = 2; }\n }\n\n newSchema.properties = properties;\n }\n\n // Revove v1-v2 boolean 'optional' key\n if (typeof newSchema.optional === 'boolean') {\n delete newSchema.optional;\n changed = true;\n if (!draft) { draft = 2; }\n }\n\n // Revove v1-v2 'requires' key\n if (newSchema.requires) {\n delete newSchema.requires;\n }\n\n // Revove v3 boolean 'required' key\n if (typeof newSchema.required === 'boolean') {\n delete newSchema.required;\n }\n\n // Convert id to $id\n if (typeof newSchema.id === 'string' && !newSchema.$id) {\n if (newSchema.id.slice(-1) === '#') {\n newSchema.id = newSchema.id.slice(0, -1);\n }\n newSchema.$id = newSchema.id + '-CONVERTED-TO-DRAFT-06#';\n delete newSchema.id;\n changed = true;\n }\n\n // Check if v1-v3 'any' or object types will be converted\n if (newSchema.type && (typeof newSchema.type.every === 'function' ?\n !newSchema.type.every(type => simpleTypes.includes(type)) :\n !simpleTypes.includes(newSchema.type)\n )) {\n changed = true;\n }\n\n // If schema changed, update or remove $schema identifier\n if (typeof newSchema.$schema === 'string' &&\n /http\\:\\/\\/json\\-schema\\.org\\/draft\\-0[1-4]\\/schema\\#/.test(newSchema.$schema)\n ) {\n newSchema.$schema = 'http://json-schema.org/draft-06/schema#';\n changed = true;\n } else if (changed && typeof newSchema.$schema === 'string') {\n const addToDescription = 'Converted to draft 6 from ' + newSchema.$schema;\n if (typeof newSchema.description === 'string' && newSchema.description.length) {\n newSchema.description += '\\n' + addToDescription;\n } else {\n newSchema.description = addToDescription;\n }\n delete newSchema.$schema;\n }\n\n // Convert v1-v3 'any' and object types\n if (newSchema.type && (typeof newSchema.type.every === 'function' ?\n !newSchema.type.every(type => simpleTypes.includes(type)) :\n !simpleTypes.includes(newSchema.type)\n )) {\n if (newSchema.type.length === 1) { newSchema.type = newSchema.type[0]; }\n if (typeof newSchema.type === 'string') {\n // Convert string 'any' type to array of all standard types\n if (newSchema.type === 'any') {\n newSchema.type = simpleTypes;\n // Delete non-standard string type\n } else {\n delete newSchema.type;\n }\n } else if (typeof newSchema.type === 'object') {\n if (typeof newSchema.type.every === 'function') {\n // If array of strings, only allow standard types\n if (newSchema.type.every(type => typeof type === 'string')) {\n newSchema.type = newSchema.type.some(type => type === 'any') ?\n newSchema.type = simpleTypes :\n newSchema.type.filter(type => simpleTypes.includes(type));\n // If type is an array with objects, convert the current schema to an 'anyOf' array\n } else if (newSchema.type.length > 1) {\n const arrayKeys = ['additionalItems', 'items', 'maxItems', 'minItems', 'uniqueItems', 'contains'];\n const numberKeys = ['multipleOf', 'maximum', 'exclusiveMaximum', 'minimum', 'exclusiveMinimum'];\n const objectKeys = ['maxProperties', 'minProperties', 'required', 'additionalProperties',\n 'properties', 'patternProperties', 'dependencies', 'propertyNames'];\n const stringKeys = ['maxLength', 'minLength', 'pattern', 'format'];\n const filterKeys = {\n 'array': [...numberKeys, ...objectKeys, ...stringKeys],\n 'integer': [...arrayKeys, ...objectKeys, ...stringKeys],\n 'number': [...arrayKeys, ...objectKeys, ...stringKeys],\n 'object': [...arrayKeys, ...numberKeys, ...stringKeys],\n 'string': [...arrayKeys, ...numberKeys, ...objectKeys],\n 'all': [...arrayKeys, ...numberKeys, ...objectKeys, ...stringKeys],\n };\n const anyOf = [];\n for (const type of newSchema.type) {\n const newType = typeof type === 'string' ? { type } : { ...type };\n Object.keys(newSchema)\n .filter(key => !newType.hasOwnProperty(key) &&\n ![...(filterKeys[newType.type] || filterKeys.all), 'type', 'default']\n .includes(key)\n )\n .forEach(key => newType[key] = newSchema[key]);\n anyOf.push(newType);\n }\n newSchema = newSchema.hasOwnProperty('default') ?\n { anyOf, default: newSchema.default } : { anyOf };\n // If type is an object, merge it with the current schema\n } else {\n const typeSchema = newSchema.type;\n delete newSchema.type;\n Object.assign(newSchema, typeSchema);\n }\n }\n } else {\n delete newSchema.type;\n }\n }\n\n // Convert sub schemas\n Object.keys(newSchema)\n .filter(key => typeof newSchema[key] === 'object')\n .forEach(key => {\n if (\n ['definitions', 'dependencies', 'properties', 'patternProperties']\n .includes(key) && typeof newSchema[key].map !== 'function'\n ) {\n const newKey = {};\n Object.keys(newSchema[key]).forEach(subKey => newKey[subKey] =\n convertSchemaToDraft6(newSchema[key][subKey], { changed, draft })\n );\n newSchema[key] = newKey;\n } else if (\n ['items', 'additionalItems', 'additionalProperties',\n 'allOf', 'anyOf', 'oneOf', 'not'].includes(key)\n ) {\n newSchema[key] = convertSchemaToDraft6(newSchema[key], { changed, draft });\n } else {\n newSchema[key] = cloneDeep(newSchema[key]);\n }\n });\n\n return newSchema;\n}\n","import { AbstractControl } from '@angular/forms';\nimport { from, Observable } from 'rxjs';\n\n/**\n * Validator utility function library:\n *\n * Validator and error utilities:\n * _executeValidators, _executeAsyncValidators, _mergeObjects, _mergeErrors\n *\n * Individual value checking:\n * isDefined, hasValue, isEmpty\n *\n * Individual type checking:\n * isString, isNumber, isInteger, isBoolean, isFunction, isObject, isArray,\n * isMap, isSet, isPromise, isObservable\n *\n * Multiple type checking and fixing:\n * getType, isType, isPrimitive, toJavaScriptType, toSchemaType,\n * _toPromise, toObservable\n *\n * Utility functions:\n * inArray, xor\n *\n * Typescript types and interfaces:\n * SchemaPrimitiveType, SchemaType, JavaScriptPrimitiveType, JavaScriptType,\n * PrimitiveValue, PlainObject, IValidatorFn, AsyncIValidatorFn\n *\n * Note: 'IValidatorFn' is short for 'invertable validator function',\n * which is a validator functions that accepts an optional second\n * argument which, if set to TRUE, causes the validator to perform\n * the opposite of its original function.\n */\n\nexport type SchemaPrimitiveType =\n 'string' | 'number' | 'integer' | 'boolean' | 'null';\nexport type SchemaType =\n 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'object' | 'array';\nexport type JavaScriptPrimitiveType =\n 'string' | 'number' | 'boolean' | 'null' | 'undefined';\nexport type JavaScriptType =\n 'string' | 'number' | 'boolean' | 'null' | 'undefined' | 'object' | 'array' |\n 'map' | 'set' | 'arguments' | 'date' | 'error' | 'function' | 'json' |\n 'math' | 'regexp'; // Note: this list is incomplete\nexport type PrimitiveValue = string | number | boolean | null | undefined;\nexport interface PlainObject { [k: string]: any; }\n\nexport type IValidatorFn = (c: AbstractControl, i?: boolean) => PlainObject;\nexport type AsyncIValidatorFn = (c: AbstractControl, i?: boolean) => any;\n\n/**\n * '_executeValidators' utility function\n *\n * Validates a control against an array of validators, and returns\n * an array of the same length containing a combination of error messages\n * (from invalid validators) and null values (from valid validators)\n *\n * // { AbstractControl } control - control to validate\n * // { IValidatorFn[] } validators - array of validators\n * // { boolean } invert - invert?\n * // { PlainObject[] } - array of nulls and error message\n */\nexport function _executeValidators(control, validators, invert = false) {\n return validators.map(validator => validator(control, invert));\n}\n\n/**\n * '_executeAsyncValidators' utility function\n *\n * Validates a control against an array of async validators, and returns\n * an array of observabe results of the same length containing a combination of\n * error messages (from invalid validators) and null values (from valid ones)\n *\n * // { AbstractControl } control - control to validate\n * // { AsyncIValidatorFn[] } validators - array of async validators\n * // { boolean } invert - invert?\n * // - array of observable nulls and error message\n */\nexport function _executeAsyncValidators(control, validators, invert = false) {\n return validators.map(validator => validator(control, invert));\n}\n\n/**\n * '_mergeObjects' utility function\n *\n * Recursively Merges one or more objects into a single object with combined keys.\n * Automatically detects and ignores null and undefined inputs.\n * Also detects duplicated boolean 'not' keys and XORs their values.\n *\n * // { PlainObject[] } objects - one or more objects to merge\n * // { PlainObject } - merged object\n */\nexport function _mergeObjects(...objects) {\n const mergedObject: PlainObject = { };\n for (const currentObject of objects) {\n if (isObject(currentObject)) {\n for (const key of Object.keys(currentObject)) {\n const currentValue = currentObject[key];\n const mergedValue = mergedObject[key];\n mergedObject[key] = !isDefined(mergedValue) ? currentValue :\n key === 'not' && isBoolean(mergedValue, 'strict') &&\n isBoolean(currentValue, 'strict') ? xor(mergedValue, currentValue) :\n getType(mergedValue) === 'object' && getType(currentValue) === 'object' ?\n _mergeObjects(mergedValue, currentValue) :\n currentValue;\n }\n }\n }\n return mergedObject;\n}\n\n/**\n * '_mergeErrors' utility function\n *\n * Merges an array of objects.\n * Used for combining the validator errors returned from 'executeValidators'\n *\n * // { PlainObject[] } arrayOfErrors - array of objects\n * // { PlainObject } - merged object, or null if no usable input objectcs\n */\nexport function _mergeErrors(arrayOfErrors) {\n const mergedErrors = _mergeObjects(...arrayOfErrors);\n return isEmpty(mergedErrors) ? null : mergedErrors;\n}\n\n/**\n * 'isDefined' utility function\n *\n * Checks if a variable contains a value of any type.\n * Returns true even for otherwise 'falsey' values of 0, '', and false.\n *\n * // value - the value to check\n * // { boolean } - false if undefined or null, otherwise true\n */\nexport function isDefined(value) {\n return value !== undefined && value !== null;\n}\n\n/**\n * 'hasValue' utility function\n *\n * Checks if a variable contains a value.\n * Returs false for null, undefined, or a zero-length strng, '',\n * otherwise returns true.\n * (Stricter than 'isDefined' because it also returns false for '',\n * though it stil returns true for otherwise 'falsey' values 0 and false.)\n *\n * // value - the value to check\n * // { boolean } - false if undefined, null, or '', otherwise true\n */\nexport function hasValue(value) {\n return value !== undefined && value !== null && value !== '';\n}\n\n/**\n * 'isEmpty' utility function\n *\n * Similar to !hasValue, but also returns true for empty arrays and objects.\n *\n * // value - the value to check\n * // { boolean } - false if undefined, null, or '', otherwise true\n */\nexport function isEmpty(value) {\n if (isArray(value)) { return !value.length; }\n if (isObject(value)) { return !Object.keys(value).length; }\n return value === undefined || value === null || value === '';\n}\n\n/**\n * 'isString' utility function\n *\n * Checks if a value is a string.\n *\n * // value - the value to check\n * // { boolean } - true if string, false if not\n */\nexport function isString(value) {\n return typeof value === 'string';\n}\n\n/**\n * 'isNumber' utility function\n *\n * Checks if a value is a regular number, numeric string, or JavaScript Date.\n *\n * // value - the value to check\n * // { any = false } strict - if truthy, also checks JavaScript tyoe\n * // { boolean } - true if number, false if not\n */\nexport function isNumber(value, strict: any = false) {\n if (strict && typeof value !== 'number') { return false; }\n return !isNaN(value) && value !== value / 0;\n}\n\n/**\n * 'isInteger' utility function\n *\n * Checks if a value is an integer.\n *\n * // value - the value to check\n * // { any = false } strict - if truthy, also checks JavaScript tyoe\n * // {boolean } - true if number, false if not\n */\nexport function isInteger(value, strict: any = false) {\n if (strict && typeof value !== 'number') { return false; }\n return !isNaN(value) && value !== value / 0 && value % 1 === 0;\n}\n\n/**\n * 'isBoolean' utility function\n *\n * Checks if a value is a boolean.\n *\n * // value - the value to check\n * // { any = null } option - if 'strict', also checks JavaScript type\n * if TRUE or FALSE, checks only for that value\n * // { boolean } - true if boolean, false if not\n */\nexport function isBoolean(value, option: any = null) {\n if (option === 'strict') { return value === true || value === false; }\n if (option === true) {\n return value === true || value === 1 || value === 'true' || value === '1';\n }\n if (option === false) {\n return value === false || value === 0 || value === 'false' || value === '0';\n }\n return value === true || value === 1 || value === 'true' || value === '1' ||\n value === false || value === 0 || value === 'false' || value === '0';\n}\n\nexport function isFunction(item: any): boolean {\n return typeof item === 'function';\n}\n\nexport function isObject(item: any): boolean {\n return item !== null && typeof item === 'object';\n}\n\nexport function isArray(item: any): boolean {\n return Array.isArray(item);\n}\n\nexport function isDate(item: any): boolean {\n return !!item && Object.prototype.toString.call(item) === '[object Date]';\n}\n\nexport function isMap(item: any): boolean {\n return !!item && Object.prototype.toString.call(item) === '[object Map]';\n}\n\nexport function isSet(item: any): boolean {\n return !!item && Object.prototype.toString.call(item) === '[object Set]';\n}\n\nexport function isSymbol(item: any): boolean {\n return typeof item === 'symbol';\n}\n\n/**\n * 'getType' function\n *\n * Detects the JSON Schema Type of a value.\n * By default, detects numbers and integers even if formatted as strings.\n * (So all integers are also numbers, and any number may also be a string.)\n * However, it only detects true boolean values (to detect boolean values\n * in non-boolean formats, use isBoolean() instead).\n *\n * If passed a second optional parameter of 'strict', it will only detect\n * numbers and integers if they are formatted as JavaScript numbers.\n *\n * Examples:\n * getType('10.5') = 'number'\n * getType(10.5) = 'number'\n * getType('10') = 'integer'\n * getType(10) = 'integer'\n * getType('true') = 'string'\n * getType(true) = 'boolean'\n * getType(null) = 'null'\n * getType({ }) = 'object'\n * getType([]) = 'array'\n *\n * getType('10.5', 'strict') = 'string'\n * getType(10.5, 'strict') = 'number'\n * getType('10', 'strict') = 'string'\n * getType(10, 'strict') = 'integer'\n * getType('true', 'strict') = 'string'\n * getType(true, 'strict') = 'boolean'\n *\n * // value - value to check\n * // { any = false } strict - if truthy, also checks JavaScript tyoe\n * // { SchemaType }\n */\nexport function getType(value, strict: any = false) {\n if (!isDefined(value)) { return 'null'; }\n if (isArray(value)) { return 'array'; }\n if (isObject(value)) { return 'object'; }\n if (isBoolean(value, 'strict')) { return 'boolean'; }\n if (isInteger(value, strict)) { return 'integer'; }\n if (isNumber(value, strict)) { return 'number'; }\n if (isString(value) || (!strict && isDate(value))) { return 'string'; }\n return null;\n}\n\n/**\n * 'isType' function\n *\n * Checks wether an input (probably string) value contains data of\n * a specified JSON Schema type\n *\n * // { PrimitiveValue } value - value to check\n * // { SchemaPrimitiveType } type - type to check\n * // { boolean }\n */\nexport function isType(value, type) {\n switch (type) {\n case 'string':\n return isString(value) || isDate(value);\n case 'number':\n return isNumber(value);\n case 'integer':\n return isInteger(value);\n case 'boolean':\n return isBoolean(value);\n case 'null':\n return !hasValue(value);\n default:\n console.error(`isType error: \"${type}\" is not a recognized type.`);\n return null;\n }\n}\n\n/**\n * 'isPrimitive' function\n *\n * Checks wether an input value is a JavaScript primitive type:\n * string, number, boolean, or null.\n *\n * // value - value to check\n * // { boolean }\n */\nexport function isPrimitive(value) {\n return (isString(value) || isNumber(value) ||\n isBoolean(value, 'strict') || value === null);\n}\n\n/**\n * \n * @param date \n * @returns {string}\n * exmaple:\n * toDateString('2018-01-01') = '2018-01-01'\n * toDateString('2018-01-30T00:00:00.000Z') = '2018-01-30'\n */\nexport const toIsoString = (date: Date) => {\n const day = date.getDate();\n const month = date.getMonth() + 1;\n const year = date.getFullYear();\n return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`;\n}\n\n/**\n * 'toJavaScriptType' function\n *\n * Converts an input (probably string) value to a JavaScript primitive type -\n * 'string', 'number', 'boolean', or 'null' - before storing in a JSON object.\n *\n * Does not coerce values (other than null), and only converts the types\n * of values that would otherwise be valid.\n *\n * If the optional third parameter 'strictIntegers' is TRUE, and the\n * JSON Schema type 'integer' is specified, it also verifies the input value\n * is an integer and, if it is, returns it as a JaveScript number.\n * If 'strictIntegers' is FALSE (or not set) the type 'integer' is treated\n * exactly the same as 'number', and allows decimals.\n *\n * Valid Examples:\n * toJavaScriptType('10', 'number' ) = 10 // '10' is a number\n * toJavaScriptType('10', 'integer') = 10 // '10' is also an integer\n * toJavaScriptType( 10, 'integer') = 10 // 10 is still an integer\n * toJavaScriptType( 10, 'string' ) = '10' // 10 can be made into a string\n * toJavaScriptType('10.5', 'number' ) = 10.5 // '10.5' is a number\n *\n * Invalid Examples:\n * toJavaScriptType('10.5', 'integer') = null // '10.5' is not an integer\n * toJavaScriptType( 10.5, 'integer') = null // 10.5 is still not an integer\n *\n * // { PrimitiveValue } value - value to convert\n * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - types to convert to\n * // { boolean = false } strictIntegers - if FALSE, treat integers as numbers\n * // { PrimitiveValue }\n */\nexport function toJavaScriptType(value, types, strictIntegers = true) {\n if (!isDefined(value)) { return null; }\n if (isString(types)) { types = [types]; }\n if (strictIntegers && inArray('integer', types)) {\n if (isInteger(value, 'strict')) { return value; }\n if (isInteger(value)) { return parseInt(value, 10); }\n }\n if (inArray('number', types) || (!strictIntegers && inArray('integer', types))) {\n if (isNumber(value, 'strict')) { return value; }\n if (isNumber(value)) { return parseFloat(value); }\n }\n if (inArray('string', types)) {\n if (isString(value)) { return value; }\n // If value is a date, and types includes 'string',\n // convert the date to a string\n if (isDate(value)) { return toIsoString(value); }\n if (isNumber(value)) { return value.toString(); }\n }\n // If value is a date, and types includes 'integer' or 'number',\n // but not 'string', convert the date to a number\n if (isDate(value) && (inArray('integer', types) || inArray('number', types))) {\n return value.getTime();\n }\n if (inArray('boolean', types)) {\n if (isBoolean(value, true)) { return true; }\n if (isBoolean(value, false)) { return false; }\n }\n return null;\n}\n\n/**\n * 'toSchemaType' function\n *\n * Converts an input (probably string) value to the \"best\" JavaScript\n * equivalent available from an allowed list of JSON Schema types, which may\n * contain 'string', 'number', 'integer', 'boolean', and/or 'null'.\n * If necssary, it does progressively agressive type coersion.\n * It will not return null unless null is in the list of allowed types.\n *\n * Number conversion examples:\n * toSchemaType('10', ['number','integer','string']) = 10 // integer\n * toSchemaType('10', ['number','string']) = 10 // number\n * toSchemaType('10', ['string']) = '10' // string\n * toSchemaType('10.5', ['number','integer','string']) = 10.5 // number\n * toSchemaType('10.5', ['integer','string']) = '10.5' // string\n * toSchemaType('10.5', ['integer']) = 10 // integer\n * toSchemaType(10.5, ['null','boolean','string']) = '10.5' // string\n * toSchemaType(10.5, ['null','boolean']) = true // boolean\n *\n * String conversion examples:\n * toSchemaType('1.5x', ['boolean','number','integer','string']) = '1.5x' // string\n * toSchemaType('1.5x', ['boolean','number','integer']) = '1.5' // number\n * toSchemaType('1.5x', ['boolean','integer']) = '1' // integer\n * toSchemaType('1.5x', ['boolean']) = true // boolean\n * toSchemaType('xyz', ['number','integer','boolean','null']) = true // boolean\n * toSchemaType('xyz', ['number','integer','null']) = null // null\n * toSchemaType('xyz', ['number','integer']) = 0 // number\n *\n * Boolean conversion examples:\n * toSchemaType('1', ['integer','number','string','boolean']) = 1 // integer\n * toSchemaType('1', ['number','string','boolean']) = 1 // number\n * toSchemaType('1', ['string','boolean']) = '1' // string\n * toSchemaType('1', ['boolean']) = true // boolean\n * toSchemaType('true', ['number','string','boolean']) = 'true' // string\n * toSchemaType('true', ['boolean']) = true // boolean\n * toSchemaType('true', ['number']) = 0 // number\n * toSchemaType(true, ['number','string','boolean']) = true // boolean\n * toSchemaType(true, ['number','string']) = 'true' // string\n * toSchemaType(true, ['number']) = 1 // number\n *\n * // { PrimitiveValue } value - value to convert\n * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - allowed types to convert to\n * // { PrimitiveValue }\n */\nexport function toSchemaType(value, types) {\n if (!isArray(<SchemaPrimitiveType>types)) {\n types = <SchemaPrimitiveType[]>[types];\n }\n if ((<SchemaPrimitiveType[]>types).includes('null') && !hasValue(value)) {\n return null;\n }\n if ((<SchemaPrimitiveType[]>types).includes('boolean') && !isBoolean(value, 'strict')) {\n return value;\n }\n if ((<SchemaPrimitiveType[]>types).includes('integer')) {\n const testValue = toJavaScriptType(value, 'integer');\n if (testValue !== null) { return +testValue; }\n }\n if ((<SchemaPrimitiveType[]>types).includes('number')) {\n const testValue = toJavaScriptType(value, 'number');\n if (testValue !== null) { return +testValue; }\n }\n if (\n (isString(value) || isNumber(value, 'strict')) &&\n (<SchemaPrimitiveType[]>types).includes('string')\n ) { // Convert number to string\n return toJavaScriptType(value, 'string');\n }\n if ((<SchemaPrimitiveType[]>types).includes('boolean') && isBoolean(value)) {\n return toJavaScriptType(value, 'boolean');\n }\n if ((<SchemaPrimitiveType[]>types).includes('string')) { // Convert null & boolean to string\n if (value === null) { return ''; }\n const testValue = toJavaScriptType(value, 'string');\n if (testValue !== null) { return testValue; }\n }\n if ((\n (<SchemaPrimitiveType[]>types).includes('number') ||\n (<SchemaPrimitiveType[]>types).includes('integer'))\n ) {\n if (value === true) { return 1; } // Convert boolean & null to number\n if (value === false || value === null || value === '') { return 0; }\n }\n if ((<SchemaPrimitiveType[]>types).includes('number')) { // Convert mixed string to number\n const testValue = parseFloat(<string>value);\n if (!!testValue) { return testValue; }\n }\n if ((<SchemaPrimitiveType[]>types).includes('integer')) { // Convert string or number to integer\n const testValue = parseInt(<string>value, 10);\n if (!!testValue) { return testValue; }\n }\n if ((<SchemaPrimitiveType[]>types).includes('boolean')) { // Convert anything to boolean\n return !!value;\n }\n if ((\n (<SchemaPrimitiveType[]>types).includes('number') ||\n (<SchemaPrimitiveType[]>types).includes('integer')\n ) && !(<SchemaPrimitiveType[]>types).includes('null')\n ) {\n return 0; // If null not allowed, return 0 for non-convertable values\n }\n}\n\n/**\n * 'isPromise' function\n *\n * // object\n * // { boolean }\n */\nexport function isPromise(object): object is Promise<any> {\n return !!object && typeof object.then === 'function';\n}\n\n/**\n * 'isObservable' function\n *\n * // object\n * // { boolean }\n */\nexport function isObservable(object): object is Observable<any> {\n return !!object && typeof object.subscribe === 'function';\n}\n\n/**\n * '_toPromise' function\n *\n * // { object } object\n * // { Promise<any> }\n */\nexport function _toPromise(object): Promise<any> {\n return isPromise(object) ? object : object.toPromise();\n}\n\n/**\n * 'toObservable' function\n *\n * // { object } object\n * // { Observable<any> }\n */\nexport function toObservable(object): Observable<any> {\n const observable = isPromise(object) ? from(object) : object;\n if (isObservable(observable)) { return observable; }\n console.error('toObservable error: Expected validator to return Promise or Observable.');\n return new Observable();\n}\n\n/**\n * 'inArray' function\n *\n * Searches an array for an item, or one of a list of items, and returns true\n * as soon as a match is found, or false if no match.\n *\n * If the optional third parameter allIn is set to TRUE, and the item to find\n * is an array, then the function returns true only if all elements from item\n * are found in the array list, and false if any element is not found. If the\n * item to find is not an array, setting allIn to TRUE has no effect.\n *\n * // { any|any[] } item - the item to search for\n * // array - the array to search\n * // { boolean = false } allIn - if TRUE, all items must be in array\n * // { boolean } - true if item(s) in array, false otherwise\n */\nexport function inArray(item, array, allIn = false) {\n if (!isDefined(item) || !isArray(array)) { return false; }\n return isArray(item) ?\n item[allIn ? 'every' : 'some'](subItem => array.includes(subItem)) :\n array.includes(item);\n}\n\n/**\n * 'xor' utility function - exclusive or\n *\n * Returns true if exactly one of two values is truthy.\n *\n * // value1 - first value to check\n * // value2 - second value to check\n * // { boolean } - true if exactly one input value is truthy, false if not\n */\nexport function xor(value1, value2) {\n return (!!value1 && !value2) || (!value1 && !!value2);\n}\n","import {hasValue, inArray, isArray, isDefined, isEmpty, isMap, isObject, isSet, isString, PlainObject} from './validator.functions';\n\n/**\n * Utility function library:\n *\n * addClasses, copy, forEach, forEachCopy, hasOwn, mergeFilteredObject,\n * uniqueItems, commonItems, fixTitle, toTitleCase\n*/\n\n/**\n * 'addClasses' function\n *\n * Merges two space-delimited lists of CSS classes and removes duplicates.\n *\n * // {string | string[] | Set<string>} oldClasses\n * // {string | string[] | Set<string>} newClasses\n * // {string | string[] | Set<string>} - Combined classes\n */\nexport function addClasses(\n oldClasses: string | string[] | Set<string>,\n newClasses: string | string[] | Set<string>\n): string | string[] | Set<string> {\n const badType = i => !isSet(i) && !isArray(i) && !isString(i);\n if (badType(newClasses)) { return oldClasses; }\n if (badType(oldClasses)) { oldClasses = ''; }\n const toSet = i => isSet(i) ? i : isArray(i) ? new Set(i) : new Set(i.split(' '));\n const combinedSet: Set<any> = toSet(oldClasses);\n const newSet: Set<any> = toSet(newClasses);\n newSet.forEach(c => combinedSet.add(c));\n if (isSet(oldClasses)) { return combinedSet; }\n if (isArray(oldClasses)) { return Array.from(combinedSet); }\n return Array.from(combinedSet).join(' ');\n}\n\n/**\n * 'copy' function\n *\n * Makes a shallow copy of a JavaScript object, array, Map, or Set.\n * If passed a JavaScript primitive value (string, number, boolean, or null),\n * it returns the value.\n *\n * // {Object|Array|string|number|boolean|null} object - The object to copy\n * // {boolean = false} errors - Show errors?\n * // {Object|Array|string|number|boolean|null} - The copied object\n */\nexport function copy(object: any, errors = false): any {\n if (typeof object !== 'object' || object === null) { return object; }\n if (isMap(object)) { return new Map(object); }\n if (isSet(object)) { return new Set(object); }\n if (isArray(object)) { return [ ...object ]; }\n if (isObject(object)) { return { ...object }; }\n if (errors) {\n console.error('copy error: Object to copy must be a JavaScript object or value.');\n }\n return object;\n}\n\n/**\n * 'forEach' function\n *\n * Iterates over all items in the first level of an object or array\n * and calls an iterator funciton on each item.\n *\n * The iterator function is called with four values:\n * 1. The current item's value\n * 2. The current item's key\n * 3. The parent object, which contains the current item\n * 4. The root object\n *\n * Setting the optional third parameter to 'top-down' or 'bottom-up' will cause\n * it to also recursively iterate over items in sub-objects or sub-arrays in the\n * specified direction.\n *\n * // {Object|Array} object - The object or array to iterate over\n * // {function} fn - the iterator funciton to call on each item\n * // {boolean = false} errors - Show errors?\n * // {void}\n */\nexport function forEach(\n object: any, fn: (v: any, k?: string | number, c?: any, rc?: any) => any,\n recurse: boolean | string = false, rootObject: any = object, errors = false\n): void {\n if (isEmpty(object)) { return; }\n if ((isObject(object) || isArray(object)) && typeof fn === 'function') {\n for (const key of Object.keys(object)) {\n const value = object[key];\n if (recurse === 'bottom-up' && (isObject(value) || isArray(value))) {\n forEach(value, fn, recurse, rootObject);\n }\n fn(value, key, object, rootObject);\n if (recurse === 'top-down' && (isObject(value) || isArray(value))) {\n forEach(value, fn, recurse, rootObject);\n }\n }\n }\n if (errors) {\n if (typeof fn !== 'function') {\n console.error('forEach error: Iterator must be a function.');\n console.error('function', fn);\n }\n if (!isObject(object) && !isArray(object)) {\n console.error('forEach error: Input object must be an object or array.');\n console.error('object', object);\n }\n }\n}\n\n/**\n * 'forEachCopy' function\n *\n * Iterates over all items in the first level of an object or array\n * and calls an iterator function on each item. Returns a new object or array\n * with the same keys or indexes as the original, and values set to the results\n * of the iterator function.\n *\n * Does NOT recursively iterate over items in sub-objects or sub-arrays.\n *\n * // {Object | Array} object - The object or array to iterate over\n * // {function} fn - The iterator funciton to call on each item\n * // {boolean = false} errors - Show errors?\n * // {Object | Array} - The resulting object or array\n */\nexport function forEachCopy(\n object: any, fn: (v: any, k?: string | number, o?: any, p?: string) => any,\n errors = false\n): any {\n if (!hasValue(object)) { return; }\n if ((isObject(object) || isArray(object)) && typeof object !== 'function') {\n const newObject: any = isArray(object) ? [] : {};\n for (const key of Object.keys(object)) {\n newObject[key] = fn(object[key], key, object);\n }\n return newObject;\n }\n if (errors) {\n if (typeof fn !== 'function') {\n console.error('forEachCopy error: Iterator must be a function.');\n console.error('function', fn);\n }\n if (!isObject(object) && !isArray(object)) {\n console.error('forEachCopy error: Input object must be an object or array.');\n console.error('object', object);\n }\n }\n}\n\n/**\n * 'hasOwn' utility function\n *\n * Checks whether an object or array has a particular property.\n *\n * // {any} object - the object to check\n * // {string} property - the property to look for\n * // {boolean} - true if object has property, false if not\n */\nexport function hasOwn(object: any, property: string): boolean {\n if (!object || !['number', 'string', 'symbol'].includes(typeof property) ||\n (!isObject(object) && !isArray(object) && !isMap(object) && !isSet(object))\n ) { return false; }\n if (isMap(object) || isSet(object)) { return object.has(property); }\n if (typeof property === 'number') {\n if (isArray(object)) { return object[<number>property]; }\n property = property + '';\n }\n return object.hasOwnProperty(property);\n}\n\n/**\n * Types of possible expressions which the app is able to evaluate.\n */\nexport enum ExpressionType {\n EQUALS,\n NOT_EQUALS,\n NOT_AN_EXPRESSION\n}\n\n/**\n * Detects the type of expression from the given candidate. `==` for equals,\n * `!=` for not equals. If none of these are contained in the candidate, the candidate\n * is not considered to be an expression at all and thus `NOT_AN_EXPRESSION` is returned.\n * // {expressionCandidate} expressionCandidate - potential expression\n */\nexport function getExpressionType(expressionCandidate: string): ExpressionType {\n if (expressionCandidate.indexOf('==') !== -1) {\n return ExpressionType.EQUALS;\n }\n\n if (expressionCandidate.toString().indexOf('!=') !== -1) {\n return ExpressionType.NOT_EQUALS;\n }\n\n return ExpressionType.NOT_AN_EXPRESSION;\n}\n\nexport function isEqual(expressionType) {\n return expressionType as ExpressionType === ExpressionType.EQUALS;\n}\n\nexport function isNotEqual(expressionType) {\n return expressionType as ExpressionType === ExpressionType.NOT_EQUALS;\n}\n\nexport function isNotExpression(expressionType) {\n return expressionType as ExpressionType === ExpressionType.NOT_AN_EXPRESSION;\n}\n\n/**\n * Splits the expression key by the expressionType on a pair of values\n * before and after the equals or nor equals sign.\n * // {expressionType} enum of an expression type\n * // {key} the given key from a for loop iver all conditions\n */\nexport function getKeyAndValueByExpressionType(expressionType: ExpressionType, key: string) {\n if (isEqual(expressionType)) {\n return key.split('==', 2);\n }\n\n if (isNotEqual(expressionType)) {\n return key.split('!=', 2);\n }\n\n return null;\n}\n\nexport function cleanValueOfQuotes(keyAndValue): String {\n if (keyAndValue.charAt(0) === '\\'' && keyAndValue.charAt(keyAndValue.length - 1) === '\\'') {\n return keyAndValue.replace('\\'', '').replace('\\'', '');\n }\n return keyAndValue;\n}\n\n/**\n * 'mergeFilteredObject' utility function\n *\n * Shallowly merges two objects, setting key and values from source object\n * in target object, excluding specified keys.\n *\n * Optionally, it can also use functions to transform the key names and/or\n * the values of the merging object.\n *\n * // {PlainObject} targetObject - Target object to add keys and values to\n * // {PlainObject} sourceObject - Source object to copy keys and values from\n * // {string[]} excludeKeys - Array of keys to exclude\n * // {(string: string) => string = (k) => k} keyFn - Function to apply to keys\n * // {(any: any) => any = (v) => v} valueFn - Function to apply to values\n * // {PlainObject} - Returns targetObject\n */\nexport function mergeFilteredObject(\n targetObject: PlainObject,\n sourceObject: PlainObject,\n excludeKeys = <string[]>[],\n keyFn = (key: string): string => key,\n valFn = (val: any): any => val\n): PlainObject {\n if (!isObject(sourceObject)) { return targetObject; }\n if (!isObject(targetObject)) { targetObject = {}; }\n for (const key of Object.keys(sourceObject)) {\n if (!inArray(key, excludeKeys) && isDefined(sourceObject[key])) {\n targetObject[keyFn(key)] = valFn(sourceObject[key]);\n }\n }\n return targetObject;\n}\n\n/**\n * 'uniqueItems' function\n *\n * Accepts any number of string value inputs,\n * and returns an array of all input vaues, excluding duplicates.\n *\n * // {...string} ...items -\n * // {string[]} -\n */\nexport function uniqueItems(...items): string[] {\n const returnItems = [];\n for (const item of items) {\n if (!returnItems.includes(item)) { returnItems.push(item); }\n }\n return returnItems;\n}\n\n/**\n * 'commonItems' function\n *\n * Accepts any number of strings or arrays of string values,\n * and returns a single array containing only values present in all inputs.\n *\n * // {...string|string[]} ...arrays -\n * // {string[]} -\n */\nexport function commonItems(...arrays): string[] {\n let returnItems = null;\n for (let array of arrays) {\n if (isString(array)) { array = [array]; }\n returnItems = returnItems === null ? [ ...array ] :\n returnItems.filter(item => array.includes(item));\n if (!returnItems.length) { return []; }\n }\n return returnItems;\n}\n\n/**\n * 'fixTitle' function\n *\n *\n * // {string} input -\n * // {string} -\n */\nexport function fixTitle(name: string): string {\n return name && toTitleCase(name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/_/g, ' '));\n}\n\n/**\n * 'toTitleCase' function\n *\n * Intelligently converts an input string to Title Case.\n *\n * Accepts an optional second parameter with a list of additional\n * words and abbreviations to force into a particular case.\n *\n * This function is built on prior work by John Gruber and David Gouch:\n * http://daringfireball.net/2008/08/title_case_update\n * https://github.com/gouch/to-title-case\n *\n * // {string} inp