@ng-formworks/core 17.5.2 → 17.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/esm2022/lib/json-schema-form.component.mjs +7 -6
  2. package/esm2022/lib/json-schema-form.module.mjs +3 -3
  3. package/esm2022/lib/json-schema-form.service.mjs +25 -5
  4. package/esm2022/lib/widget-library/element-attribute.directive.mjs +33 -0
  5. package/esm2022/lib/widget-library/index.mjs +5 -4
  6. package/esm2022/lib/widget-library/input.component.mjs +22 -9
  7. package/esm2022/lib/widget-library/number.component.mjs +37 -15
  8. package/esm2022/lib/widget-library/orderable.directive.mjs +11 -2
  9. package/esm2022/lib/widget-library/root.component.mjs +106 -35
  10. package/esm2022/lib/widget-library/section.component.mjs +9 -9
  11. package/esm2022/lib/widget-library/select-framework.component.mjs +3 -4
  12. package/esm2022/lib/widget-library/select-widget.component.mjs +3 -4
  13. package/esm2022/lib/widget-library/template.component.mjs +3 -4
  14. package/esm2022/lib/widget-library/widget-library.module.mjs +23 -8
  15. package/fesm2022/ng-formworks-core.mjs +1731 -1556
  16. package/fesm2022/ng-formworks-core.mjs.map +1 -1
  17. package/lib/json-schema-form.service.d.ts +7 -1
  18. package/lib/shared/validator.functions.d.ts +1 -1
  19. package/lib/widget-library/element-attribute.directive.d.ts +13 -0
  20. package/lib/widget-library/index.d.ts +3 -2
  21. package/lib/widget-library/input.component.d.ts +2 -1
  22. package/lib/widget-library/number.component.d.ts +4 -1
  23. package/lib/widget-library/orderable.directive.d.ts +4 -2
  24. package/lib/widget-library/root.component.d.ts +8 -1
  25. package/lib/widget-library/select-framework.component.d.ts +0 -1
  26. package/lib/widget-library/select-widget.component.d.ts +0 -1
  27. package/lib/widget-library/template.component.d.ts +0 -1
  28. package/lib/widget-library/widget-library.module.d.ts +5 -3
  29. package/package.json +4 -2
@@ -1,1401 +1,1416 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
- import * as i2 from '@angular/forms';
4
- import { UntypedFormControl, UntypedFormArray, UntypedFormGroup, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms';
5
3
  import * as i0 from '@angular/core';
6
- import { Injectable, inject, input, ChangeDetectionStrategy, Component, ComponentFactoryResolver, viewChild, ViewContainerRef, ElementRef, NgZone, Directive, signal, Inject, forwardRef, ChangeDetectorRef, output, Input, NgModule } from '@angular/core';
7
- import cloneDeep from 'lodash/cloneDeep';
8
- import isEqual$1 from 'lodash/isEqual';
9
- import { from, Observable, forkJoin, Subject, lastValueFrom } from 'rxjs';
10
- import { map, takeUntil } from 'rxjs/operators';
11
- import { HttpClient } from '@angular/common/http';
4
+ import { Injectable, inject, input, viewChild, ViewContainerRef, Component, Input, Directive, ChangeDetectionStrategy, ViewChild, ElementRef, NgZone, signal, NgModule, Inject, forwardRef, ChangeDetectorRef, output } from '@angular/core';
5
+ import * as i2 from '@angular/forms';
6
+ import { UntypedFormControl, UntypedFormArray, UntypedFormGroup, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
12
7
  import Ajv2019 from 'ajv/dist/2019';
13
8
  import jsonDraft6 from 'ajv/lib/refs/json-schema-draft-06.json';
14
9
  import jsonDraft7 from 'ajv/lib/refs/json-schema-draft-07.json';
10
+ import cloneDeep from 'lodash/cloneDeep';
11
+ import { from, Observable, forkJoin, Subject, BehaviorSubject, lastValueFrom } from 'rxjs';
12
+ import isEqual$1 from 'lodash/isEqual';
13
+ import { map, takeUntil } from 'rxjs/operators';
15
14
  import filter from 'lodash/filter';
16
15
  import map$1 from 'lodash/map';
17
16
  import _isArray from 'lodash/isArray';
18
17
  import _isPlainObject from 'lodash/isPlainObject';
19
18
  import uniqueId from 'lodash/uniqueId';
19
+ import * as i2$1 from 'nxt-sortablejs';
20
+ import { SortablejsModule } from 'nxt-sortablejs';
21
+ import { HttpClient } from '@angular/common/http';
20
22
 
21
- /**
22
- * '_executeValidators' utility function
23
- *
24
- * Validates a control against an array of validators, and returns
25
- * an array of the same length containing a combination of error messages
26
- * (from invalid validators) and null values (from valid validators)
27
- *
28
- * // { AbstractControl } control - control to validate
29
- * // { IValidatorFn[] } validators - array of validators
30
- * // { boolean } invert - invert?
31
- * // { PlainObject[] } - array of nulls and error message
32
- */
33
- function _executeValidators(control, validators, invert = false) {
34
- return validators.map(validator => validator(control, invert));
35
- }
36
- /**
37
- * '_executeAsyncValidators' utility function
38
- *
39
- * Validates a control against an array of async validators, and returns
40
- * an array of observabe results of the same length containing a combination of
41
- * error messages (from invalid validators) and null values (from valid ones)
42
- *
43
- * // { AbstractControl } control - control to validate
44
- * // { AsyncIValidatorFn[] } validators - array of async validators
45
- * // { boolean } invert - invert?
46
- * // - array of observable nulls and error message
47
- */
48
- function _executeAsyncValidators(control, validators, invert = false) {
49
- return validators.map(validator => validator(control, invert));
50
- }
51
- /**
52
- * '_mergeObjects' utility function
53
- *
54
- * Recursively Merges one or more objects into a single object with combined keys.
55
- * Automatically detects and ignores null and undefined inputs.
56
- * Also detects duplicated boolean 'not' keys and XORs their values.
57
- *
58
- * // { PlainObject[] } objects - one or more objects to merge
59
- * // { PlainObject } - merged object
60
- */
61
- function _mergeObjects(...objects) {
62
- const mergedObject = {};
63
- for (const currentObject of objects) {
64
- if (isObject(currentObject)) {
65
- for (const key of Object.keys(currentObject)) {
66
- const currentValue = currentObject[key];
67
- const mergedValue = mergedObject[key];
68
- mergedObject[key] = !isDefined(mergedValue) ? currentValue :
69
- key === 'not' && isBoolean(mergedValue, 'strict') &&
70
- isBoolean(currentValue, 'strict') ? xor(mergedValue, currentValue) :
71
- getType(mergedValue) === 'object' && getType(currentValue) === 'object' ?
72
- _mergeObjects(mergedValue, currentValue) :
73
- currentValue;
74
- }
75
- }
76
- }
77
- return mergedObject;
78
- }
79
- /**
80
- * '_mergeErrors' utility function
81
- *
82
- * Merges an array of objects.
83
- * Used for combining the validator errors returned from 'executeValidators'
84
- *
85
- * // { PlainObject[] } arrayOfErrors - array of objects
86
- * // { PlainObject } - merged object, or null if no usable input objectcs
87
- */
88
- function _mergeErrors(arrayOfErrors) {
89
- const mergedErrors = _mergeObjects(...arrayOfErrors);
90
- return isEmpty(mergedErrors) ? null : mergedErrors;
91
- }
92
- /**
93
- * 'isDefined' utility function
94
- *
95
- * Checks if a variable contains a value of any type.
96
- * Returns true even for otherwise 'falsey' values of 0, '', and false.
97
- *
98
- * // value - the value to check
99
- * // { boolean } - false if undefined or null, otherwise true
100
- */
101
- function isDefined(value) {
102
- return value !== undefined && value !== null;
103
- }
104
- /**
105
- * 'hasValue' utility function
106
- *
107
- * Checks if a variable contains a value.
108
- * Returs false for null, undefined, or a zero-length strng, '',
109
- * otherwise returns true.
110
- * (Stricter than 'isDefined' because it also returns false for '',
111
- * though it stil returns true for otherwise 'falsey' values 0 and false.)
112
- *
113
- * // value - the value to check
114
- * // { boolean } - false if undefined, null, or '', otherwise true
115
- */
116
- function hasValue(value) {
117
- return value !== undefined && value !== null && value !== '';
118
- }
119
- /**
120
- * 'isEmpty' utility function
121
- *
122
- * Similar to !hasValue, but also returns true for empty arrays and objects.
123
- *
124
- * // value - the value to check
125
- * // { boolean } - false if undefined, null, or '', otherwise true
126
- */
127
- function isEmpty(value) {
128
- if (isArray(value)) {
129
- return !value.length;
130
- }
131
- if (isObject(value)) {
132
- return !Object.keys(value).length;
133
- }
134
- return value === undefined || value === null || value === '';
135
- }
136
- /**
137
- * 'isString' utility function
138
- *
139
- * Checks if a value is a string.
140
- *
141
- * // value - the value to check
142
- * // { boolean } - true if string, false if not
143
- */
144
- function isString(value) {
145
- return typeof value === 'string';
146
- }
147
- /**
148
- * 'isNumber' utility function
149
- *
150
- * Checks if a value is a regular number, numeric string, or JavaScript Date.
151
- *
152
- * // value - the value to check
153
- * // { any = false } strict - if truthy, also checks JavaScript tyoe
154
- * // { boolean } - true if number, false if not
155
- */
156
- function isNumber(value, strict = false) {
157
- if (strict && typeof value !== 'number') {
158
- return false;
159
- }
160
- return !isNaN(value) && value !== value / 0;
161
- }
162
- /**
163
- * 'isInteger' utility function
164
- *
165
- * Checks if a value is an integer.
166
- *
167
- * // value - the value to check
168
- * // { any = false } strict - if truthy, also checks JavaScript tyoe
169
- * // {boolean } - true if number, false if not
170
- */
171
- function isInteger(value, strict = false) {
172
- if (strict && typeof value !== 'number') {
173
- return false;
174
- }
175
- return !isNaN(value) && value !== value / 0 && value % 1 === 0;
176
- }
177
- /**
178
- * 'isBoolean' utility function
179
- *
180
- * Checks if a value is a boolean.
181
- *
182
- * // value - the value to check
183
- * // { any = null } option - if 'strict', also checks JavaScript type
184
- * if TRUE or FALSE, checks only for that value
185
- * // { boolean } - true if boolean, false if not
186
- */
187
- function isBoolean(value, option = null) {
188
- if (option === 'strict') {
189
- return value === true || value === false;
190
- }
191
- if (option === true) {
192
- return value === true || value === 1 || value === 'true' || value === '1';
193
- }
194
- if (option === false) {
195
- return value === false || value === 0 || value === 'false' || value === '0';
23
+ class Framework {
24
+ constructor() {
25
+ this.widgets = {};
26
+ this.stylesheets = [];
27
+ this.scripts = [];
196
28
  }
197
- return value === true || value === 1 || value === 'true' || value === '1' ||
198
- value === false || value === 0 || value === 'false' || value === '0';
199
- }
200
- function isFunction(item) {
201
- return typeof item === 'function';
202
- }
203
- function isObject(item) {
204
- return item !== null && typeof item === 'object';
205
- }
206
- function isArray(item) {
207
- return Array.isArray(item);
208
- }
209
- function isDate(item) {
210
- return !!item && Object.prototype.toString.call(item) === '[object Date]';
211
- }
212
- function isMap(item) {
213
- return !!item && Object.prototype.toString.call(item) === '[object Map]';
214
- }
215
- function isSet(item) {
216
- return !!item && Object.prototype.toString.call(item) === '[object Set]';
217
- }
218
- function isSymbol(item) {
219
- return typeof item === 'symbol';
29
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: Framework, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
30
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: Framework }); }
220
31
  }
221
- /**
222
- * 'getType' function
223
- *
224
- * Detects the JSON Schema Type of a value.
225
- * By default, detects numbers and integers even if formatted as strings.
226
- * (So all integers are also numbers, and any number may also be a string.)
227
- * However, it only detects true boolean values (to detect boolean values
228
- * in non-boolean formats, use isBoolean() instead).
229
- *
230
- * If passed a second optional parameter of 'strict', it will only detect
231
- * numbers and integers if they are formatted as JavaScript numbers.
232
- *
233
- * Examples:
234
- * getType('10.5') = 'number'
235
- * getType(10.5) = 'number'
236
- * getType('10') = 'integer'
237
- * getType(10) = 'integer'
238
- * getType('true') = 'string'
239
- * getType(true) = 'boolean'
240
- * getType(null) = 'null'
241
- * getType({ }) = 'object'
242
- * getType([]) = 'array'
243
- *
244
- * getType('10.5', 'strict') = 'string'
245
- * getType(10.5, 'strict') = 'number'
246
- * getType('10', 'strict') = 'string'
247
- * getType(10, 'strict') = 'integer'
248
- * getType('true', 'strict') = 'string'
249
- * getType(true, 'strict') = 'boolean'
250
- *
251
- * // value - value to check
252
- * // { any = false } strict - if truthy, also checks JavaScript tyoe
253
- * // { SchemaType }
254
- */
255
- function getType(value, strict = false) {
256
- if (!isDefined(value)) {
257
- return 'null';
258
- }
259
- if (isArray(value)) {
260
- return 'array';
261
- }
262
- if (isObject(value)) {
263
- return 'object';
264
- }
265
- if (isBoolean(value, 'strict')) {
266
- return 'boolean';
267
- }
268
- if (isInteger(value, strict)) {
269
- return 'integer';
270
- }
271
- if (isNumber(value, strict)) {
272
- return 'number';
273
- }
274
- if (isString(value) || (!strict && isDate(value))) {
275
- return 'string';
276
- }
277
- return null;
278
- }
279
- /**
280
- * 'isType' function
281
- *
282
- * Checks wether an input (probably string) value contains data of
283
- * a specified JSON Schema type
284
- *
285
- * // { PrimitiveValue } value - value to check
286
- * // { SchemaPrimitiveType } type - type to check
287
- * // { boolean }
288
- */
289
- function isType(value, type) {
290
- switch (type) {
291
- case 'string':
292
- return isString(value) || isDate(value);
293
- case 'number':
294
- return isNumber(value);
295
- case 'integer':
296
- return isInteger(value);
297
- case 'boolean':
298
- return isBoolean(value);
299
- case 'null':
300
- return !hasValue(value);
301
- default:
302
- console.error(`isType error: "${type}" is not a recognized type.`);
303
- return null;
304
- }
305
- }
306
- /**
307
- * 'isPrimitive' function
308
- *
309
- * Checks wether an input value is a JavaScript primitive type:
310
- * string, number, boolean, or null.
311
- *
312
- * // value - value to check
313
- * // { boolean }
314
- */
315
- function isPrimitive(value) {
316
- return (isString(value) || isNumber(value) ||
317
- isBoolean(value, 'strict') || value === null);
318
- }
319
- /**
320
- *
321
- * @param date
322
- * @returns {string}
323
- * exmaple:
324
- * toDateString('2018-01-01') = '2018-01-01'
325
- * toDateString('2018-01-30T00:00:00.000Z') = '2018-01-30'
326
- */
327
- const toIsoString = (date) => {
328
- const day = date.getDate();
329
- const month = date.getMonth() + 1;
330
- const year = date.getFullYear();
331
- return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`;
32
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: Framework, decorators: [{
33
+ type: Injectable
34
+ }] });
35
+
36
+ const deValidationMessages = {
37
+ required: 'Darf nicht leer sein',
38
+ minLength: 'Mindestens {{minimumLength}} Zeichen benötigt (aktuell: {{currentLength}})',
39
+ maxLength: 'Maximal {{maximumLength}} Zeichen erlaubt (aktuell: {{currentLength}})',
40
+ pattern: 'Entspricht nicht diesem regulären Ausdruck: {{requiredPattern}}',
41
+ format: function (error) {
42
+ switch (error.requiredFormat) {
43
+ case 'date':
44
+ return 'Muss ein Datum sein, z. B. "2000-12-31"';
45
+ case 'time':
46
+ return 'Muss eine Zeitangabe sein, z. B. "16:20" oder "03:14:15.9265"';
47
+ case 'date-time':
48
+ return 'Muss Datum mit Zeit beinhalten, z. B. "2000-03-14T01:59" oder "2000-03-14T01:59:26.535Z"';
49
+ case 'email':
50
+ return 'Keine gültige E-Mail-Adresse (z. B. "name@example.com")';
51
+ case 'hostname':
52
+ return 'Kein gültiger Hostname (z. B. "example.com")';
53
+ case 'ipv4':
54
+ return 'Keine gültige IPv4-Adresse (z. B. "127.0.0.1")';
55
+ case 'ipv6':
56
+ return 'Keine gültige IPv6-Adresse (z. B. "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0")';
57
+ // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
58
+ // case 'uri': case 'uri-reference': case 'uri-template':
59
+ case 'url':
60
+ return 'Keine gültige URL (z. B. "http://www.example.com/page.html")';
61
+ case 'uuid':
62
+ return 'Keine gültige UUID (z. B. "12345678-9ABC-DEF0-1234-56789ABCDEF0")';
63
+ case 'color':
64
+ return 'Kein gültiger Farbwert (z. B. "#FFFFFF" oder "rgb(255, 255, 255)")';
65
+ case 'json-pointer':
66
+ return 'Kein gültiger JSON-Pointer (z. B. "/pointer/to/something")';
67
+ case 'relative-json-pointer':
68
+ return 'Kein gültiger relativer JSON-Pointer (z. B. "2/pointer/to/something")';
69
+ case 'regex':
70
+ return 'Kein gültiger regulärer Ausdruck (z. B. "(1-)?\\d{3}-\\d{3}-\\d{4}")';
71
+ default:
72
+ return 'Muss diesem Format entsprechen: ' + error.requiredFormat;
73
+ }
74
+ },
75
+ minimum: 'Muss mindestens {{minimumValue}} sein',
76
+ exclusiveMinimum: 'Muss größer als {{exclusiveMinimumValue}} sein',
77
+ maximum: 'Darf maximal {{maximumValue}} sein',
78
+ exclusiveMaximum: 'Muss kleiner als {{exclusiveMaximumValue}} sein',
79
+ multipleOf: function (error) {
80
+ if ((1 / error.multipleOfValue) % 10 === 0) {
81
+ const decimals = Math.log10(1 / error.multipleOfValue);
82
+ return `Maximal ${decimals} Dezimalstellen erlaubt`;
83
+ }
84
+ else {
85
+ return `Muss ein Vielfaches von ${error.multipleOfValue} sein`;
86
+ }
87
+ },
88
+ minProperties: 'Mindestens {{minimumProperties}} Attribute erforderlich (aktuell: {{currentProperties}})',
89
+ maxProperties: 'Maximal {{maximumProperties}} Attribute erlaubt (aktuell: {{currentProperties}})',
90
+ minItems: 'Mindestens {{minimumItems}} Werte erforderlich (aktuell: {{currentItems}})',
91
+ maxItems: 'Maximal {{maximumItems}} Werte erlaubt (aktuell: {{currentItems}})',
92
+ uniqueItems: 'Alle Werte müssen eindeutig sein',
93
+ // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
332
94
  };
333
- /**
334
- * 'toJavaScriptType' function
335
- *
336
- * Converts an input (probably string) value to a JavaScript primitive type -
337
- * 'string', 'number', 'boolean', or 'null' - before storing in a JSON object.
338
- *
339
- * Does not coerce values (other than null), and only converts the types
340
- * of values that would otherwise be valid.
341
- *
342
- * If the optional third parameter 'strictIntegers' is TRUE, and the
343
- * JSON Schema type 'integer' is specified, it also verifies the input value
344
- * is an integer and, if it is, returns it as a JaveScript number.
345
- * If 'strictIntegers' is FALSE (or not set) the type 'integer' is treated
346
- * exactly the same as 'number', and allows decimals.
347
- *
348
- * Valid Examples:
349
- * toJavaScriptType('10', 'number' ) = 10 // '10' is a number
350
- * toJavaScriptType('10', 'integer') = 10 // '10' is also an integer
351
- * toJavaScriptType( 10, 'integer') = 10 // 10 is still an integer
352
- * toJavaScriptType( 10, 'string' ) = '10' // 10 can be made into a string
353
- * toJavaScriptType('10.5', 'number' ) = 10.5 // '10.5' is a number
354
- *
355
- * Invalid Examples:
356
- * toJavaScriptType('10.5', 'integer') = null // '10.5' is not an integer
357
- * toJavaScriptType( 10.5, 'integer') = null // 10.5 is still not an integer
358
- *
359
- * // { PrimitiveValue } value - value to convert
360
- * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - types to convert to
361
- * // { boolean = false } strictIntegers - if FALSE, treat integers as numbers
362
- * // { PrimitiveValue }
363
- */
364
- function toJavaScriptType(value, types, strictIntegers = true) {
365
- if (!isDefined(value)) {
366
- return null;
367
- }
368
- if (isString(types)) {
369
- types = [types];
370
- }
371
- if (strictIntegers && inArray('integer', types)) {
372
- if (isInteger(value, 'strict')) {
373
- return value;
95
+
96
+ const enValidationMessages = {
97
+ required: 'This field is required.',
98
+ minLength: 'Must be {{minimumLength}} characters or longer (current length: {{currentLength}})',
99
+ maxLength: 'Must be {{maximumLength}} characters or shorter (current length: {{currentLength}})',
100
+ pattern: 'Must match pattern: {{requiredPattern}}',
101
+ format: function (error) {
102
+ switch (error.requiredFormat) {
103
+ case 'date':
104
+ return 'Must be a date, like "2000-12-31"';
105
+ case 'time':
106
+ return 'Must be a time, like "16:20" or "03:14:15.9265"';
107
+ case 'date-time':
108
+ return 'Must be a date-time, like "2000-03-14T01:59" or "2000-03-14T01:59:26.535Z"';
109
+ case 'email':
110
+ return 'Must be an email address, like "name@example.com"';
111
+ case 'hostname':
112
+ return 'Must be a hostname, like "example.com"';
113
+ case 'ipv4':
114
+ return 'Must be an IPv4 address, like "127.0.0.1"';
115
+ case 'ipv6':
116
+ return 'Must be an IPv6 address, like "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
117
+ // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
118
+ // case 'uri': case 'uri-reference': case 'uri-template':
119
+ case 'url':
120
+ return 'Must be a url, like "http://www.example.com/page.html"';
121
+ case 'uuid':
122
+ return 'Must be a uuid, like "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
123
+ case 'color':
124
+ return 'Must be a color, like "#FFFFFF" or "rgb(255, 255, 255)"';
125
+ case 'json-pointer':
126
+ return 'Must be a JSON Pointer, like "/pointer/to/something"';
127
+ case 'relative-json-pointer':
128
+ return 'Must be a relative JSON Pointer, like "2/pointer/to/something"';
129
+ case 'regex':
130
+ return 'Must be a regular expression, like "(1-)?\\d{3}-\\d{3}-\\d{4}"';
131
+ default:
132
+ return 'Must be a correctly formatted ' + error.requiredFormat;
374
133
  }
375
- if (isInteger(value)) {
376
- return parseInt(value, 10);
134
+ },
135
+ minimum: 'Must be {{minimumValue}} or more',
136
+ exclusiveMinimum: 'Must be more than {{exclusiveMinimumValue}}',
137
+ maximum: 'Must be {{maximumValue}} or less',
138
+ exclusiveMaximum: 'Must be less than {{exclusiveMaximumValue}}',
139
+ multipleOf: function (error) {
140
+ if ((1 / error.multipleOfValue) % 10 === 0) {
141
+ const decimals = Math.log10(1 / error.multipleOfValue);
142
+ return `Must have ${decimals} or fewer decimal places.`;
377
143
  }
378
- }
379
- if (inArray('number', types) || (!strictIntegers && inArray('integer', types))) {
380
- if (isNumber(value, 'strict')) {
381
- return value;
144
+ else {
145
+ return `Must be a multiple of ${error.multipleOfValue}.`;
382
146
  }
383
- if (isNumber(value)) {
384
- return parseFloat(value);
147
+ },
148
+ minProperties: 'Must have {{minimumProperties}} or more items (current items: {{currentProperties}})',
149
+ maxProperties: 'Must have {{maximumProperties}} or fewer items (current items: {{currentProperties}})',
150
+ minItems: 'Must have {{minimumItems}} or more items (current items: {{currentItems}})',
151
+ maxItems: 'Must have {{maximumItems}} or fewer items (current items: {{currentItems}})',
152
+ uniqueItems: 'All items must be unique',
153
+ // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
154
+ };
155
+
156
+ const esValidationMessages = {
157
+ required: 'Este campo está requerido.',
158
+ minLength: 'Debe tener {{minimumLength}} caracteres o más longitud (longitud actual: {{currentLength}})',
159
+ maxLength: 'Debe tener {{maximumLength}} caracteres o menos longitud (longitud actual: {{currentLength}})',
160
+ pattern: 'Must match pattern: {{requiredPattern}}',
161
+ format: function (error) {
162
+ switch (error.requiredFormat) {
163
+ case 'date':
164
+ return 'Debe tener una fecha, ej "2000-12-31"';
165
+ case 'time':
166
+ return 'Debe tener una hora, ej "16:20" o "03:14:15.9265"';
167
+ case 'date-time':
168
+ return 'Debe tener fecha y hora, ej "2000-03-14T01:59" o "2000-03-14T01:59:26.535Z"';
169
+ case 'email':
170
+ return 'No hay dirección de correo electrónico válida, ej "name@example.com"';
171
+ case 'hostname':
172
+ return 'Debe ser un nombre de host válido, ej "example.com"';
173
+ case 'ipv4':
174
+ return 'Debe ser una dirección de IPv4, ej "127.0.0.1"';
175
+ case 'ipv6':
176
+ return 'Debe ser una dirección de IPv6, ej "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
177
+ case 'url':
178
+ return 'Debe ser una URL, ej "http://www.example.com/page.html"';
179
+ case 'uuid':
180
+ return 'Debe ser un UUID, ej "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
181
+ case 'color':
182
+ return 'Debe ser un color, ej "#FFFFFF" or "rgb(255, 255, 255)"';
183
+ case 'json-pointer':
184
+ return 'Debe ser un JSON Pointer, ej "/pointer/to/something"';
185
+ case 'relative-json-pointer':
186
+ return 'Debe ser un JSON Pointer relativo, ej "2/pointer/to/something"';
187
+ case 'regex':
188
+ return 'Debe ser una expresión regular, ej "(1-)?\\d{3}-\\d{3}-\\d{4}"';
189
+ default:
190
+ return 'Debe tener el formato correcto ' + error.requiredFormat;
191
+ }
192
+ },
193
+ minimum: 'Debe ser {{minimumValue}} o más',
194
+ exclusiveMinimum: 'Debe ser superior a {{exclusiveMinimumValue}}',
195
+ maximum: 'Debe ser {{maximumValue}} o menos',
196
+ exclusiveMaximum: 'Debe ser menor que {{exclusiveMaximumValue}}',
197
+ multipleOf: function (error) {
198
+ if ((1 / error.multipleOfValue) % 10 === 0) {
199
+ const decimals = Math.log10(1 / error.multipleOfValue);
200
+ return `Se permite un máximo de ${decimals} decimales`;
201
+ }
202
+ else {
203
+ return `Debe ser múltiplo de ${error.multipleOfValue}.`;
204
+ }
205
+ },
206
+ minProperties: 'Debe tener {{minimumProperties}} o más elementos (elementos actuales: {{currentProperties}})',
207
+ maxProperties: 'Debe tener {{maximumProperties}} o menos elementos (elementos actuales: {{currentProperties}})',
208
+ minItems: 'Debe tener {{minimumItems}} o más elementos (elementos actuales: {{currentItems}})',
209
+ maxItems: 'Debe tener {{maximumItems}} o menos elementos (elementos actuales: {{currentItems}})',
210
+ uniqueItems: 'Todos los elementos deben ser únicos',
211
+ };
212
+
213
+ const frValidationMessages = {
214
+ required: 'Est obligatoire.',
215
+ minLength: 'Doit avoir minimum {{minimumLength}} caractères (actuellement: {{currentLength}})',
216
+ maxLength: 'Doit avoir maximum {{maximumLength}} caractères (actuellement: {{currentLength}})',
217
+ pattern: 'Doit respecter: {{requiredPattern}}',
218
+ format: function (error) {
219
+ switch (error.requiredFormat) {
220
+ case 'date':
221
+ return 'Doit être une date, tel que "2000-12-31"';
222
+ case 'time':
223
+ return 'Doit être une heure, tel que "16:20" ou "03:14:15.9265"';
224
+ case 'date-time':
225
+ return 'Doit être une date et une heure, tel que "2000-03-14T01:59" ou "2000-03-14T01:59:26.535Z"';
226
+ case 'email':
227
+ return 'Doit être une adresse e-mail, tel que "name@example.com"';
228
+ case 'hostname':
229
+ return 'Doit être un nom de domaine, tel que "example.com"';
230
+ case 'ipv4':
231
+ return 'Doit être une adresse IPv4, tel que "127.0.0.1"';
232
+ case 'ipv6':
233
+ return 'Doit être une adresse IPv6, tel que "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
234
+ // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
235
+ // case 'uri': case 'uri-reference': case 'uri-template':
236
+ case 'url':
237
+ return 'Doit être une URL, tel que "http://www.example.com/page.html"';
238
+ case 'uuid':
239
+ return 'Doit être un UUID, tel que "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
240
+ case 'color':
241
+ return 'Doit être une couleur, tel que "#FFFFFF" or "rgb(255, 255, 255)"';
242
+ case 'json-pointer':
243
+ return 'Doit être un JSON Pointer, tel que "/pointer/to/something"';
244
+ case 'relative-json-pointer':
245
+ return 'Doit être un relative JSON Pointer, tel que "2/pointer/to/something"';
246
+ case 'regex':
247
+ return 'Doit être une expression régulière, tel que "(1-)?\\d{3}-\\d{3}-\\d{4}"';
248
+ default:
249
+ return 'Doit être avoir le format correct: ' + error.requiredFormat;
250
+ }
251
+ },
252
+ minimum: 'Doit être supérieur à {{minimumValue}}',
253
+ exclusiveMinimum: 'Doit avoir minimum {{exclusiveMinimumValue}} charactères',
254
+ maximum: 'Doit être inférieur à {{maximumValue}}',
255
+ exclusiveMaximum: 'Doit avoir maximum {{exclusiveMaximumValue}} charactères',
256
+ multipleOf: function (error) {
257
+ if ((1 / error.multipleOfValue) % 10 === 0) {
258
+ const decimals = Math.log10(1 / error.multipleOfValue);
259
+ return `Doit comporter ${decimals} ou moins de decimales.`;
260
+ }
261
+ else {
262
+ return `Doit être un multiple de ${error.multipleOfValue}.`;
263
+ }
264
+ },
265
+ minProperties: 'Doit comporter au minimum {{minimumProperties}} éléments',
266
+ maxProperties: 'Doit comporter au maximum {{maximumProperties}} éléments',
267
+ minItems: 'Doit comporter au minimum {{minimumItems}} éléments',
268
+ maxItems: 'Doit comporter au maximum {{minimumItems}} éléments',
269
+ uniqueItems: 'Tous les éléments doivent être uniques',
270
+ // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
271
+ };
272
+
273
+ const itValidationMessages = {
274
+ required: 'Il campo è obbligatorio',
275
+ minLength: 'Deve inserire almeno {{minimumLength}} caratteri (lunghezza corrente: {{currentLength}})',
276
+ maxLength: 'Il numero massimo di caratteri consentito è {{maximumLength}} (lunghezza corrente: {{currentLength}})',
277
+ pattern: 'Devi rispettare il pattern : {{requiredPattern}}',
278
+ format: function (error) {
279
+ switch (error.requiredFormat) {
280
+ case 'date':
281
+ return 'Deve essere una data, come "31-12-2000"';
282
+ case 'time':
283
+ return 'Deve essere un orario, come "16:20" o "03:14:15.9265"';
284
+ case 'date-time':
285
+ return 'Deve essere data-orario, come "14-03-2000T01:59" or "14-03-2000T01:59:26.535Z"';
286
+ case 'email':
287
+ return 'Deve essere un indirzzo email, come "name@example.com"';
288
+ case 'hostname':
289
+ return 'Deve essere un hostname, come "example.com"';
290
+ case 'ipv4':
291
+ return 'Deve essere un indirizzo IPv4, come "127.0.0.1"';
292
+ case 'ipv6':
293
+ return 'Deve essere un indirizzo IPv6, come "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
294
+ // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
295
+ // case 'uri': case 'uri-reference': case 'uri-template':
296
+ case 'url':
297
+ return 'Deve essere un url, come "http://www.example.com/page.html"';
298
+ case 'uuid':
299
+ return 'Deve essere un uuid, come "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
300
+ case 'color':
301
+ return 'Deve essere un colore, come "#FFFFFF" o "rgb(255, 255, 255)"';
302
+ case 'json-pointer':
303
+ return 'Deve essere un JSON Pointer, come "/pointer/to/something"';
304
+ case 'relative-json-pointer':
305
+ return 'Deve essere un JSON Pointer relativo, come "2/pointer/to/something"';
306
+ case 'regex':
307
+ return 'Deve essere una regular expression, come "(1-)?\\d{3}-\\d{3}-\\d{4}"';
308
+ default:
309
+ return 'Deve essere formattato correttamente ' + error.requiredFormat;
310
+ }
311
+ },
312
+ minimum: 'Deve essere {{minimumValue}} o più',
313
+ exclusiveMinimum: 'Deve essere più di {{exclusiveMinimumValue}}',
314
+ maximum: 'Deve essere {{maximumValue}} o meno',
315
+ exclusiveMaximum: 'Deve essere minore di {{exclusiveMaximumValue}}',
316
+ multipleOf: function (error) {
317
+ if ((1 / error.multipleOfValue) % 10 === 0) {
318
+ const decimals = Math.log10(1 / error.multipleOfValue);
319
+ return `Deve avere ${decimals} o meno decimali.`;
320
+ }
321
+ else {
322
+ return `Deve essere multiplo di ${error.multipleOfValue}.`;
323
+ }
324
+ },
325
+ minProperties: 'Deve avere {{minimumProperties}} o più elementi (elementi correnti: {{currentProperties}})',
326
+ maxProperties: 'Deve avere {{maximumProperties}} o meno elementi (elementi correnti: {{currentProperties}})',
327
+ minItems: 'Deve avere {{minimumItems}} o più elementi (elementi correnti: {{currentItems}})',
328
+ maxItems: 'Deve avere {{maximumItems}} o meno elementi (elementi correnti: {{currentItems}})',
329
+ uniqueItems: 'Tutti gli elementi devono essere unici',
330
+ // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
331
+ };
332
+
333
+ const ptValidationMessages = {
334
+ required: 'Este campo é obrigatório.',
335
+ minLength: 'É preciso no mínimo {{minimumLength}} caracteres ou mais (tamanho atual: {{currentLength}})',
336
+ maxLength: 'É preciso no máximo {{maximumLength}} caracteres ou menos (tamanho atual: {{currentLength}})',
337
+ pattern: 'Tem que ajustar ao formato: {{requiredPattern}}',
338
+ format: function (error) {
339
+ switch (error.requiredFormat) {
340
+ case 'date':
341
+ return 'Tem que ser uma data, por exemplo "2000-12-31"';
342
+ case 'time':
343
+ return 'Tem que ser horário, por exemplo "16:20" ou "03:14:15.9265"';
344
+ case 'date-time':
345
+ return 'Tem que ser data e hora, por exemplo "2000-03-14T01:59" ou "2000-03-14T01:59:26.535Z"';
346
+ case 'email':
347
+ return 'Tem que ser um email, por exemplo "fulano@exemplo.com.br"';
348
+ case 'hostname':
349
+ return 'Tem que ser uma nome de domínio, por exemplo "exemplo.com.br"';
350
+ case 'ipv4':
351
+ return 'Tem que ser um endereço IPv4, por exemplo "127.0.0.1"';
352
+ case 'ipv6':
353
+ return 'Tem que ser um endereço IPv6, por exemplo "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
354
+ // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
355
+ // case 'uri': case 'uri-reference': case 'uri-template':
356
+ case 'url':
357
+ return 'Tem que ser uma URL, por exemplo "http://www.exemplo.com.br/pagina.html"';
358
+ case 'uuid':
359
+ return 'Tem que ser um uuid, por exemplo "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
360
+ case 'color':
361
+ return 'Tem que ser uma cor, por exemplo "#FFFFFF" ou "rgb(255, 255, 255)"';
362
+ case 'json-pointer':
363
+ return 'Tem que ser um JSON Pointer, por exemplo "/referencia/para/algo"';
364
+ case 'relative-json-pointer':
365
+ return 'Tem que ser um JSON Pointer relativo, por exemplo "2/referencia/para/algo"';
366
+ case 'regex':
367
+ return 'Tem que ser uma expressão regular, por exemplo "(1-)?\\d{3}-\\d{3}-\\d{4}"';
368
+ default:
369
+ return 'Tem que ser no formato: ' + error.requiredFormat;
385
370
  }
386
- }
387
- if (inArray('string', types)) {
388
- if (isString(value)) {
389
- return value;
371
+ },
372
+ minimum: 'Tem que ser {{minimumValue}} ou mais',
373
+ exclusiveMinimum: 'Tem que ser mais que {{exclusiveMinimumValue}}',
374
+ maximum: 'Tem que ser {{maximumValue}} ou menos',
375
+ exclusiveMaximum: 'Tem que ser menor que {{exclusiveMaximumValue}}',
376
+ multipleOf: function (error) {
377
+ if ((1 / error.multipleOfValue) % 10 === 0) {
378
+ const decimals = Math.log10(1 / error.multipleOfValue);
379
+ return `Tem que ter ${decimals} ou menos casas decimais.`;
390
380
  }
391
- // If value is a date, and types includes 'string',
392
- // convert the date to a string
393
- if (isDate(value)) {
394
- return toIsoString(value);
381
+ else {
382
+ return `Tem que ser um múltiplo de ${error.multipleOfValue}.`;
395
383
  }
396
- if (isNumber(value)) {
397
- return value.toString();
384
+ },
385
+ minProperties: 'Deve ter {{minimumProperties}} ou mais itens (itens até o momento: {{currentProperties}})',
386
+ maxProperties: 'Deve ter {{maximumProperties}} ou menos intens (itens até o momento: {{currentProperties}})',
387
+ minItems: 'Deve ter {{minimumItems}} ou mais itens (itens até o momento: {{currentItems}})',
388
+ maxItems: 'Deve ter {{maximumItems}} ou menos itens (itens até o momento: {{currentItems}})',
389
+ uniqueItems: 'Todos os itens devem ser únicos',
390
+ // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
391
+ };
392
+
393
+ const zhValidationMessages = {
394
+ required: '必填字段.',
395
+ minLength: '字符长度必须大于或者等于 {{minimumLength}} (当前长度: {{currentLength}})',
396
+ maxLength: '字符长度必须小于或者等于 {{maximumLength}} (当前长度: {{currentLength}})',
397
+ pattern: '必须匹配正则表达式: {{requiredPattern}}',
398
+ format: function (error) {
399
+ switch (error.requiredFormat) {
400
+ case 'date':
401
+ return '必须为日期格式, 比如 "2000-12-31"';
402
+ case 'time':
403
+ return '必须为时间格式, 比如 "16:20" 或者 "03:14:15.9265"';
404
+ case 'date-time':
405
+ return '必须为日期时间格式, 比如 "2000-03-14T01:59" 或者 "2000-03-14T01:59:26.535Z"';
406
+ case 'email':
407
+ return '必须为邮箱地址, 比如 "name@example.com"';
408
+ case 'hostname':
409
+ return '必须为主机名, 比如 "example.com"';
410
+ case 'ipv4':
411
+ return '必须为 IPv4 地址, 比如 "127.0.0.1"';
412
+ case 'ipv6':
413
+ return '必须为 IPv6 地址, 比如 "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
414
+ // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
415
+ // case 'uri': case 'uri-reference': case 'uri-template':
416
+ case 'url':
417
+ return '必须为 url, 比如 "http://www.example.com/page.html"';
418
+ case 'uuid':
419
+ return '必须为 uuid, 比如 "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
420
+ case 'color':
421
+ return '必须为颜色值, 比如 "#FFFFFF" 或者 "rgb(255, 255, 255)"';
422
+ case 'json-pointer':
423
+ return '必须为 JSON Pointer, 比如 "/pointer/to/something"';
424
+ case 'relative-json-pointer':
425
+ return '必须为相对的 JSON Pointer, 比如 "2/pointer/to/something"';
426
+ case 'regex':
427
+ return '必须为正则表达式, 比如 "(1-)?\\d{3}-\\d{3}-\\d{4}"';
428
+ default:
429
+ return '必须为格式正确的 ' + error.requiredFormat;
398
430
  }
399
- }
400
- // If value is a date, and types includes 'integer' or 'number',
401
- // but not 'string', convert the date to a number
402
- if (isDate(value) && (inArray('integer', types) || inArray('number', types))) {
403
- return value.getTime();
404
- }
405
- if (inArray('boolean', types)) {
406
- if (isBoolean(value, true)) {
407
- return true;
431
+ },
432
+ minimum: '必须大于或者等于最小值: {{minimumValue}}',
433
+ exclusiveMinimum: '必须大于最小值: {{exclusiveMinimumValue}}',
434
+ maximum: '必须小于或者等于最大值: {{maximumValue}}',
435
+ exclusiveMaximum: '必须小于最大值: {{exclusiveMaximumValue}}',
436
+ multipleOf: function (error) {
437
+ if ((1 / error.multipleOfValue) % 10 === 0) {
438
+ const decimals = Math.log10(1 / error.multipleOfValue);
439
+ return `必须有 ${decimals} 位或更少的小数位`;
408
440
  }
409
- if (isBoolean(value, false)) {
410
- return false;
441
+ else {
442
+ return `必须为 ${error.multipleOfValue} 的倍数`;
411
443
  }
412
- }
413
- return null;
414
- }
444
+ },
445
+ minProperties: '项目数必须大于或者等于 {{minimumProperties}} (当前项目数: {{currentProperties}})',
446
+ maxProperties: '项目数必须小于或者等于 {{maximumProperties}} (当前项目数: {{currentProperties}})',
447
+ minItems: '项目数必须大于或者等于 {{minimumItems}} (当前项目数: {{currentItems}})',
448
+ maxItems: '项目数必须小于或者等于 {{maximumItems}} (当前项目数: {{currentItems}})',
449
+ uniqueItems: '所有项目必须是唯一的',
450
+ // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
451
+ };
452
+
415
453
  /**
416
- * 'toSchemaType' function
417
- *
418
- * Converts an input (probably string) value to the "best" JavaScript
419
- * equivalent available from an allowed list of JSON Schema types, which may
420
- * contain 'string', 'number', 'integer', 'boolean', and/or 'null'.
421
- * If necssary, it does progressively agressive type coersion.
422
- * It will not return null unless null is in the list of allowed types.
454
+ * '_executeValidators' utility function
423
455
  *
424
- * Number conversion examples:
425
- * toSchemaType('10', ['number','integer','string']) = 10 // integer
426
- * toSchemaType('10', ['number','string']) = 10 // number
427
- * toSchemaType('10', ['string']) = '10' // string
428
- * toSchemaType('10.5', ['number','integer','string']) = 10.5 // number
429
- * toSchemaType('10.5', ['integer','string']) = '10.5' // string
430
- * toSchemaType('10.5', ['integer']) = 10 // integer
431
- * toSchemaType(10.5, ['null','boolean','string']) = '10.5' // string
432
- * toSchemaType(10.5, ['null','boolean']) = true // boolean
456
+ * Validates a control against an array of validators, and returns
457
+ * an array of the same length containing a combination of error messages
458
+ * (from invalid validators) and null values (from valid validators)
433
459
  *
434
- * String conversion examples:
435
- * toSchemaType('1.5x', ['boolean','number','integer','string']) = '1.5x' // string
436
- * toSchemaType('1.5x', ['boolean','number','integer']) = '1.5' // number
437
- * toSchemaType('1.5x', ['boolean','integer']) = '1' // integer
438
- * toSchemaType('1.5x', ['boolean']) = true // boolean
439
- * toSchemaType('xyz', ['number','integer','boolean','null']) = true // boolean
440
- * toSchemaType('xyz', ['number','integer','null']) = null // null
441
- * toSchemaType('xyz', ['number','integer']) = 0 // number
460
+ * // { AbstractControl } control - control to validate
461
+ * // { IValidatorFn[] } validators - array of validators
462
+ * // { boolean } invert - invert?
463
+ * // { PlainObject[] } - array of nulls and error message
464
+ */
465
+ function _executeValidators(control, validators, invert = false) {
466
+ return validators.map(validator => validator(control, invert));
467
+ }
468
+ /**
469
+ * '_executeAsyncValidators' utility function
442
470
  *
443
- * Boolean conversion examples:
444
- * toSchemaType('1', ['integer','number','string','boolean']) = 1 // integer
445
- * toSchemaType('1', ['number','string','boolean']) = 1 // number
446
- * toSchemaType('1', ['string','boolean']) = '1' // string
447
- * toSchemaType('1', ['boolean']) = true // boolean
448
- * toSchemaType('true', ['number','string','boolean']) = 'true' // string
449
- * toSchemaType('true', ['boolean']) = true // boolean
450
- * toSchemaType('true', ['number']) = 0 // number
451
- * toSchemaType(true, ['number','string','boolean']) = true // boolean
452
- * toSchemaType(true, ['number','string']) = 'true' // string
453
- * toSchemaType(true, ['number']) = 1 // number
471
+ * Validates a control against an array of async validators, and returns
472
+ * an array of observabe results of the same length containing a combination of
473
+ * error messages (from invalid validators) and null values (from valid ones)
454
474
  *
455
- * // { PrimitiveValue } value - value to convert
456
- * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - allowed types to convert to
457
- * // { PrimitiveValue }
475
+ * // { AbstractControl } control - control to validate
476
+ * // { AsyncIValidatorFn[] } validators - array of async validators
477
+ * // { boolean } invert - invert?
478
+ * // - array of observable nulls and error message
458
479
  */
459
- function toSchemaType(value, types) {
460
- if (!isArray(types)) {
461
- types = [types];
462
- }
463
- if (types.includes('null') && !hasValue(value)) {
464
- return null;
465
- }
466
- if (types.includes('boolean') && !isBoolean(value, 'strict')) {
467
- return value;
468
- }
469
- if (types.includes('integer')) {
470
- const testValue = toJavaScriptType(value, 'integer');
471
- if (testValue !== null) {
472
- return +testValue;
473
- }
474
- }
475
- if (types.includes('number')) {
476
- const testValue = toJavaScriptType(value, 'number');
477
- if (testValue !== null) {
478
- return +testValue;
479
- }
480
- }
481
- if ((isString(value) || isNumber(value, 'strict')) &&
482
- types.includes('string')) { // Convert number to string
483
- return toJavaScriptType(value, 'string');
484
- }
485
- if (types.includes('boolean') && isBoolean(value)) {
486
- return toJavaScriptType(value, 'boolean');
487
- }
488
- if (types.includes('string')) { // Convert null & boolean to string
489
- if (value === null) {
490
- return '';
491
- }
492
- const testValue = toJavaScriptType(value, 'string');
493
- if (testValue !== null) {
494
- return testValue;
495
- }
496
- }
497
- if ((types.includes('number') ||
498
- types.includes('integer'))) {
499
- if (value === true) {
500
- return 1;
501
- } // Convert boolean & null to number
502
- if (value === false || value === null || value === '') {
503
- return 0;
504
- }
505
- }
506
- if (types.includes('number')) { // Convert mixed string to number
507
- const testValue = parseFloat(value);
508
- if (!!testValue) {
509
- return testValue;
510
- }
511
- }
512
- if (types.includes('integer')) { // Convert string or number to integer
513
- const testValue = parseInt(value, 10);
514
- if (!!testValue) {
515
- return testValue;
516
- }
517
- }
518
- if (types.includes('boolean')) { // Convert anything to boolean
519
- return !!value;
520
- }
521
- if ((types.includes('number') ||
522
- types.includes('integer')) && !types.includes('null')) {
523
- return 0; // If null not allowed, return 0 for non-convertable values
524
- }
480
+ function _executeAsyncValidators(control, validators, invert = false) {
481
+ return validators.map(validator => validator(control, invert));
525
482
  }
526
483
  /**
527
- * 'isPromise' function
484
+ * '_mergeObjects' utility function
528
485
  *
529
- * // object
530
- * // { boolean }
486
+ * Recursively Merges one or more objects into a single object with combined keys.
487
+ * Automatically detects and ignores null and undefined inputs.
488
+ * Also detects duplicated boolean 'not' keys and XORs their values.
489
+ *
490
+ * // { PlainObject[] } objects - one or more objects to merge
491
+ * // { PlainObject } - merged object
531
492
  */
532
- function isPromise(object) {
533
- return !!object && typeof object.then === 'function';
493
+ function _mergeObjects(...objects) {
494
+ const mergedObject = {};
495
+ for (const currentObject of objects) {
496
+ if (isObject(currentObject)) {
497
+ for (const key of Object.keys(currentObject)) {
498
+ const currentValue = currentObject[key];
499
+ const mergedValue = mergedObject[key];
500
+ mergedObject[key] = !isDefined(mergedValue) ? currentValue :
501
+ key === 'not' && isBoolean(mergedValue, 'strict') &&
502
+ isBoolean(currentValue, 'strict') ? xor(mergedValue, currentValue) :
503
+ getType(mergedValue) === 'object' && getType(currentValue) === 'object' ?
504
+ _mergeObjects(mergedValue, currentValue) :
505
+ currentValue;
506
+ }
507
+ }
508
+ }
509
+ return mergedObject;
534
510
  }
535
511
  /**
536
- * 'isObservable' function
512
+ * '_mergeErrors' utility function
537
513
  *
538
- * // object
539
- * // { boolean }
514
+ * Merges an array of objects.
515
+ * Used for combining the validator errors returned from 'executeValidators'
516
+ *
517
+ * // { PlainObject[] } arrayOfErrors - array of objects
518
+ * // { PlainObject } - merged object, or null if no usable input objectcs
540
519
  */
541
- function isObservable(object) {
542
- return !!object && typeof object.subscribe === 'function';
520
+ function _mergeErrors(arrayOfErrors) {
521
+ const mergedErrors = _mergeObjects(...arrayOfErrors);
522
+ return isEmpty(mergedErrors) ? null : mergedErrors;
543
523
  }
544
524
  /**
545
- * '_toPromise' function
525
+ * 'isDefined' utility function
546
526
  *
547
- * // { object } object
548
- * // { Promise<any> }
527
+ * Checks if a variable contains a value of any type.
528
+ * Returns true even for otherwise 'falsey' values of 0, '', and false.
529
+ *
530
+ * // value - the value to check
531
+ * // { boolean } - false if undefined or null, otherwise true
549
532
  */
550
- function _toPromise(object) {
551
- return isPromise(object) ? object : object.toPromise();
533
+ function isDefined(value) {
534
+ return value !== undefined && value !== null;
552
535
  }
553
536
  /**
554
- * 'toObservable' function
537
+ * 'hasValue' utility function
555
538
  *
556
- * // { object } object
557
- * // { Observable<any> }
539
+ * Checks if a variable contains a value.
540
+ * Returs false for null, undefined, or a zero-length strng, '',
541
+ * otherwise returns true.
542
+ * (Stricter than 'isDefined' because it also returns false for '',
543
+ * though it stil returns true for otherwise 'falsey' values 0 and false.)
544
+ *
545
+ * // value - the value to check
546
+ * // { boolean } - false if undefined, null, or '', otherwise true
558
547
  */
559
- function toObservable(object) {
560
- const observable = isPromise(object) ? from(object) : object;
561
- if (isObservable(observable)) {
562
- return observable;
563
- }
564
- console.error('toObservable error: Expected validator to return Promise or Observable.');
565
- return new Observable();
548
+ function hasValue(value) {
549
+ return value !== undefined && value !== null && value !== '';
566
550
  }
567
551
  /**
568
- * 'inArray' function
569
- *
570
- * Searches an array for an item, or one of a list of items, and returns true
571
- * as soon as a match is found, or false if no match.
552
+ * 'isEmpty' utility function
572
553
  *
573
- * If the optional third parameter allIn is set to TRUE, and the item to find
574
- * is an array, then the function returns true only if all elements from item
575
- * are found in the array list, and false if any element is not found. If the
576
- * item to find is not an array, setting allIn to TRUE has no effect.
554
+ * Similar to !hasValue, but also returns true for empty arrays and objects.
577
555
  *
578
- * // { any|any[] } item - the item to search for
579
- * // array - the array to search
580
- * // { boolean = false } allIn - if TRUE, all items must be in array
581
- * // { boolean } - true if item(s) in array, false otherwise
556
+ * // value - the value to check
557
+ * // { boolean } - false if undefined, null, or '', otherwise true
582
558
  */
583
- function inArray(item, array, allIn = false) {
584
- if (!isDefined(item) || !isArray(array)) {
585
- return false;
559
+ function isEmpty(value) {
560
+ if (isArray(value)) {
561
+ return !value.length;
586
562
  }
587
- return isArray(item) ?
588
- item[allIn ? 'every' : 'some'](subItem => array.includes(subItem)) :
589
- array.includes(item);
563
+ if (isObject(value)) {
564
+ return !Object.keys(value).length;
565
+ }
566
+ return value === undefined || value === null || value === '';
590
567
  }
591
568
  /**
592
- * 'xor' utility function - exclusive or
569
+ * 'isString' utility function
593
570
  *
594
- * Returns true if exactly one of two values is truthy.
571
+ * Checks if a value is a string.
595
572
  *
596
- * // value1 - first value to check
597
- * // value2 - second value to check
598
- * // { boolean } - true if exactly one input value is truthy, false if not
573
+ * // value - the value to check
574
+ * // { boolean } - true if string, false if not
599
575
  */
600
- function xor(value1, value2) {
601
- return (!!value1 && !value2) || (!value1 && !!value2);
576
+ function isString(value) {
577
+ return typeof value === 'string';
602
578
  }
603
-
604
579
  /**
605
- * Utility function library:
606
- *
607
- * addClasses, copy, forEach, forEachCopy, hasOwn, mergeFilteredObject,
608
- * uniqueItems, commonItems, fixTitle, toTitleCase
609
- */
610
- /**
611
- * 'addClasses' function
580
+ * 'isNumber' utility function
612
581
  *
613
- * Merges two space-delimited lists of CSS classes and removes duplicates.
582
+ * Checks if a value is a regular number, numeric string, or JavaScript Date.
614
583
  *
615
- * // {string | string[] | Set<string>} oldClasses
616
- * // {string | string[] | Set<string>} newClasses
617
- * // {string | string[] | Set<string>} - Combined classes
584
+ * // value - the value to check
585
+ * // { any = false } strict - if truthy, also checks JavaScript tyoe
586
+ * // { boolean } - true if number, false if not
618
587
  */
619
- function addClasses(oldClasses, newClasses) {
620
- const badType = i => !isSet(i) && !isArray(i) && !isString(i);
621
- if (badType(newClasses)) {
622
- return oldClasses;
623
- }
624
- if (badType(oldClasses)) {
625
- oldClasses = '';
626
- }
627
- const toSet = i => isSet(i) ? i : isArray(i) ? new Set(i) : new Set(i.split(' '));
628
- const combinedSet = toSet(oldClasses);
629
- const newSet = toSet(newClasses);
630
- newSet.forEach(c => combinedSet.add(c));
631
- if (isSet(oldClasses)) {
632
- return combinedSet;
633
- }
634
- if (isArray(oldClasses)) {
635
- return Array.from(combinedSet);
588
+ function isNumber(value, strict = false) {
589
+ if (strict && typeof value !== 'number') {
590
+ return false;
636
591
  }
637
- return Array.from(combinedSet).join(' ');
592
+ return !isNaN(value) && value !== value / 0;
638
593
  }
639
594
  /**
640
- * 'copy' function
595
+ * 'isInteger' utility function
641
596
  *
642
- * Makes a shallow copy of a JavaScript object, array, Map, or Set.
643
- * If passed a JavaScript primitive value (string, number, boolean, or null),
644
- * it returns the value.
597
+ * Checks if a value is an integer.
645
598
  *
646
- * // {Object|Array|string|number|boolean|null} object - The object to copy
647
- * // {boolean = false} errors - Show errors?
648
- * // {Object|Array|string|number|boolean|null} - The copied object
599
+ * // value - the value to check
600
+ * // { any = false } strict - if truthy, also checks JavaScript tyoe
601
+ * // {boolean } - true if number, false if not
649
602
  */
650
- function copy(object, errors = false) {
651
- if (typeof object !== 'object' || object === null) {
652
- return object;
653
- }
654
- if (isMap(object)) {
655
- return new Map(object);
656
- }
657
- if (isSet(object)) {
658
- return new Set(object);
659
- }
660
- if (isArray(object)) {
661
- return [...object];
662
- }
663
- if (isObject(object)) {
664
- return { ...object };
665
- }
666
- if (errors) {
667
- console.error('copy error: Object to copy must be a JavaScript object or value.');
603
+ function isInteger(value, strict = false) {
604
+ if (strict && typeof value !== 'number') {
605
+ return false;
668
606
  }
669
- return object;
607
+ return !isNaN(value) && value !== value / 0 && value % 1 === 0;
670
608
  }
671
609
  /**
672
- * 'forEach' function
673
- *
674
- * Iterates over all items in the first level of an object or array
675
- * and calls an iterator funciton on each item.
676
- *
677
- * The iterator function is called with four values:
678
- * 1. The current item's value
679
- * 2. The current item's key
680
- * 3. The parent object, which contains the current item
681
- * 4. The root object
610
+ * 'isBoolean' utility function
682
611
  *
683
- * Setting the optional third parameter to 'top-down' or 'bottom-up' will cause
684
- * it to also recursively iterate over items in sub-objects or sub-arrays in the
685
- * specified direction.
612
+ * Checks if a value is a boolean.
686
613
  *
687
- * // {Object|Array} object - The object or array to iterate over
688
- * // {function} fn - the iterator funciton to call on each item
689
- * // {boolean = false} errors - Show errors?
690
- * // {void}
614
+ * // value - the value to check
615
+ * // { any = null } option - if 'strict', also checks JavaScript type
616
+ * if TRUE or FALSE, checks only for that value
617
+ * // { boolean } - true if boolean, false if not
691
618
  */
692
- function forEach(object, fn, recurse = false, rootObject = object, errors = false) {
693
- if (isEmpty(object)) {
694
- return;
619
+ function isBoolean(value, option = null) {
620
+ if (option === 'strict') {
621
+ return value === true || value === false;
695
622
  }
696
- if ((isObject(object) || isArray(object)) && typeof fn === 'function') {
697
- for (const key of Object.keys(object)) {
698
- const value = object[key];
699
- if (recurse === 'bottom-up' && (isObject(value) || isArray(value))) {
700
- forEach(value, fn, recurse, rootObject);
701
- }
702
- fn(value, key, object, rootObject);
703
- if (recurse === 'top-down' && (isObject(value) || isArray(value))) {
704
- forEach(value, fn, recurse, rootObject);
705
- }
706
- }
623
+ if (option === true) {
624
+ return value === true || value === 1 || value === 'true' || value === '1';
707
625
  }
708
- if (errors) {
709
- if (typeof fn !== 'function') {
710
- console.error('forEach error: Iterator must be a function.');
711
- console.error('function', fn);
712
- }
713
- if (!isObject(object) && !isArray(object)) {
714
- console.error('forEach error: Input object must be an object or array.');
715
- console.error('object', object);
716
- }
626
+ if (option === false) {
627
+ return value === false || value === 0 || value === 'false' || value === '0';
717
628
  }
629
+ return value === true || value === 1 || value === 'true' || value === '1' ||
630
+ value === false || value === 0 || value === 'false' || value === '0';
631
+ }
632
+ function isFunction(item) {
633
+ return typeof item === 'function';
634
+ }
635
+ function isObject(item) {
636
+ return item !== null && typeof item === 'object';
637
+ }
638
+ function isArray(item) {
639
+ return Array.isArray(item);
640
+ }
641
+ function isDate(item) {
642
+ return !!item && Object.prototype.toString.call(item) === '[object Date]';
643
+ }
644
+ function isMap(item) {
645
+ return !!item && Object.prototype.toString.call(item) === '[object Map]';
646
+ }
647
+ function isSet(item) {
648
+ return !!item && Object.prototype.toString.call(item) === '[object Set]';
649
+ }
650
+ function isSymbol(item) {
651
+ return typeof item === 'symbol';
718
652
  }
719
653
  /**
720
- * 'forEachCopy' function
654
+ * 'getType' function
655
+ *
656
+ * Detects the JSON Schema Type of a value.
657
+ * By default, detects numbers and integers even if formatted as strings.
658
+ * (So all integers are also numbers, and any number may also be a string.)
659
+ * However, it only detects true boolean values (to detect boolean values
660
+ * in non-boolean formats, use isBoolean() instead).
661
+ *
662
+ * If passed a second optional parameter of 'strict', it will only detect
663
+ * numbers and integers if they are formatted as JavaScript numbers.
721
664
  *
722
- * Iterates over all items in the first level of an object or array
723
- * and calls an iterator function on each item. Returns a new object or array
724
- * with the same keys or indexes as the original, and values set to the results
725
- * of the iterator function.
665
+ * Examples:
666
+ * getType('10.5') = 'number'
667
+ * getType(10.5) = 'number'
668
+ * getType('10') = 'integer'
669
+ * getType(10) = 'integer'
670
+ * getType('true') = 'string'
671
+ * getType(true) = 'boolean'
672
+ * getType(null) = 'null'
673
+ * getType({ }) = 'object'
674
+ * getType([]) = 'array'
726
675
  *
727
- * Does NOT recursively iterate over items in sub-objects or sub-arrays.
676
+ * getType('10.5', 'strict') = 'string'
677
+ * getType(10.5, 'strict') = 'number'
678
+ * getType('10', 'strict') = 'string'
679
+ * getType(10, 'strict') = 'integer'
680
+ * getType('true', 'strict') = 'string'
681
+ * getType(true, 'strict') = 'boolean'
728
682
  *
729
- * // {Object | Array} object - The object or array to iterate over
730
- * // {function} fn - The iterator funciton to call on each item
731
- * // {boolean = false} errors - Show errors?
732
- * // {Object | Array} - The resulting object or array
683
+ * // value - value to check
684
+ * // { any = false } strict - if truthy, also checks JavaScript tyoe
685
+ * // { SchemaType }
733
686
  */
734
- function forEachCopy(object, fn, errors = false) {
735
- if (!hasValue(object)) {
736
- return;
687
+ function getType(value, strict = false) {
688
+ if (!isDefined(value)) {
689
+ return 'null';
737
690
  }
738
- if ((isObject(object) || isArray(object)) && typeof object !== 'function') {
739
- const newObject = isArray(object) ? [] : {};
740
- for (const key of Object.keys(object)) {
741
- newObject[key] = fn(object[key], key, object);
742
- }
743
- return newObject;
691
+ if (isArray(value)) {
692
+ return 'array';
744
693
  }
745
- if (errors) {
746
- if (typeof fn !== 'function') {
747
- console.error('forEachCopy error: Iterator must be a function.');
748
- console.error('function', fn);
749
- }
750
- if (!isObject(object) && !isArray(object)) {
751
- console.error('forEachCopy error: Input object must be an object or array.');
752
- console.error('object', object);
753
- }
694
+ if (isObject(value)) {
695
+ return 'object';
696
+ }
697
+ if (isBoolean(value, 'strict')) {
698
+ return 'boolean';
699
+ }
700
+ if (isInteger(value, strict)) {
701
+ return 'integer';
702
+ }
703
+ if (isNumber(value, strict)) {
704
+ return 'number';
705
+ }
706
+ if (isString(value) || (!strict && isDate(value))) {
707
+ return 'string';
754
708
  }
709
+ return null;
755
710
  }
756
711
  /**
757
- * 'hasOwn' utility function
712
+ * 'isType' function
758
713
  *
759
- * Checks whether an object or array has a particular property.
714
+ * Checks wether an input (probably string) value contains data of
715
+ * a specified JSON Schema type
760
716
  *
761
- * // {any} object - the object to check
762
- * // {string} property - the property to look for
763
- * // {boolean} - true if object has property, false if not
717
+ * // { PrimitiveValue } value - value to check
718
+ * // { SchemaPrimitiveType } type - type to check
719
+ * // { boolean }
764
720
  */
765
- function hasOwn(object, property) {
766
- if (!object || !['number', 'string', 'symbol'].includes(typeof property) ||
767
- (!isObject(object) && !isArray(object) && !isMap(object) && !isSet(object))) {
768
- return false;
769
- }
770
- if (isMap(object) || isSet(object)) {
771
- return object.has(property);
772
- }
773
- if (typeof property === 'number') {
774
- if (isArray(object)) {
775
- return object[property];
776
- }
777
- property = property + '';
721
+ function isType(value, type) {
722
+ switch (type) {
723
+ case 'string':
724
+ return isString(value) || isDate(value);
725
+ case 'number':
726
+ return isNumber(value);
727
+ case 'integer':
728
+ return isInteger(value);
729
+ case 'boolean':
730
+ return isBoolean(value);
731
+ case 'null':
732
+ return !hasValue(value);
733
+ default:
734
+ console.error(`isType error: "${type}" is not a recognized type.`);
735
+ return null;
778
736
  }
779
- return object.hasOwnProperty(property);
780
737
  }
781
738
  /**
782
- * Types of possible expressions which the app is able to evaluate.
783
- */
784
- var ExpressionType;
785
- (function (ExpressionType) {
786
- ExpressionType[ExpressionType["EQUALS"] = 0] = "EQUALS";
787
- ExpressionType[ExpressionType["NOT_EQUALS"] = 1] = "NOT_EQUALS";
788
- ExpressionType[ExpressionType["NOT_AN_EXPRESSION"] = 2] = "NOT_AN_EXPRESSION";
789
- })(ExpressionType || (ExpressionType = {}));
790
- /**
791
- * Detects the type of expression from the given candidate. `==` for equals,
792
- * `!=` for not equals. If none of these are contained in the candidate, the candidate
793
- * is not considered to be an expression at all and thus `NOT_AN_EXPRESSION` is returned.
794
- * // {expressionCandidate} expressionCandidate - potential expression
739
+ * 'isPrimitive' function
740
+ *
741
+ * Checks wether an input value is a JavaScript primitive type:
742
+ * string, number, boolean, or null.
743
+ *
744
+ * // value - value to check
745
+ * // { boolean }
795
746
  */
796
- function getExpressionType(expressionCandidate) {
797
- if (expressionCandidate.indexOf('==') !== -1) {
798
- return ExpressionType.EQUALS;
799
- }
800
- if (expressionCandidate.toString().indexOf('!=') !== -1) {
801
- return ExpressionType.NOT_EQUALS;
802
- }
803
- return ExpressionType.NOT_AN_EXPRESSION;
804
- }
805
- function isEqual(expressionType) {
806
- return expressionType === ExpressionType.EQUALS;
807
- }
808
- function isNotEqual(expressionType) {
809
- return expressionType === ExpressionType.NOT_EQUALS;
810
- }
811
- function isNotExpression(expressionType) {
812
- return expressionType === ExpressionType.NOT_AN_EXPRESSION;
747
+ function isPrimitive(value) {
748
+ return (isString(value) || isNumber(value) ||
749
+ isBoolean(value, 'strict') || value === null);
813
750
  }
814
751
  /**
815
- * Splits the expression key by the expressionType on a pair of values
816
- * before and after the equals or nor equals sign.
817
- * // {expressionType} enum of an expression type
818
- * // {key} the given key from a for loop iver all conditions
752
+ *
753
+ * @param date
754
+ * @returns {string}
755
+ * exmaple:
756
+ * toDateString('2018-01-01') = '2018-01-01'
757
+ * toDateString('2018-01-30T00:00:00.000Z') = '2018-01-30'
819
758
  */
820
- function getKeyAndValueByExpressionType(expressionType, key) {
821
- if (isEqual(expressionType)) {
822
- return key.split('==', 2);
823
- }
824
- if (isNotEqual(expressionType)) {
825
- return key.split('!=', 2);
826
- }
827
- return null;
828
- }
829
- function cleanValueOfQuotes(keyAndValue) {
830
- if (keyAndValue.charAt(0) === '\'' && keyAndValue.charAt(keyAndValue.length - 1) === '\'') {
831
- return keyAndValue.replace('\'', '').replace('\'', '');
832
- }
833
- return keyAndValue;
834
- }
759
+ const toIsoString = (date) => {
760
+ const day = date.getDate();
761
+ const month = date.getMonth() + 1;
762
+ const year = date.getFullYear();
763
+ return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`;
764
+ };
835
765
  /**
836
- * 'mergeFilteredObject' utility function
766
+ * 'toJavaScriptType' function
837
767
  *
838
- * Shallowly merges two objects, setting key and values from source object
839
- * in target object, excluding specified keys.
768
+ * Converts an input (probably string) value to a JavaScript primitive type -
769
+ * 'string', 'number', 'boolean', or 'null' - before storing in a JSON object.
840
770
  *
841
- * Optionally, it can also use functions to transform the key names and/or
842
- * the values of the merging object.
771
+ * Does not coerce values (other than null), and only converts the types
772
+ * of values that would otherwise be valid.
843
773
  *
844
- * // {PlainObject} targetObject - Target object to add keys and values to
845
- * // {PlainObject} sourceObject - Source object to copy keys and values from
846
- * // {string[]} excludeKeys - Array of keys to exclude
847
- * // {(string: string) => string = (k) => k} keyFn - Function to apply to keys
848
- * // {(any: any) => any = (v) => v} valueFn - Function to apply to values
849
- * // {PlainObject} - Returns targetObject
774
+ * If the optional third parameter 'strictIntegers' is TRUE, and the
775
+ * JSON Schema type 'integer' is specified, it also verifies the input value
776
+ * is an integer and, if it is, returns it as a JaveScript number.
777
+ * If 'strictIntegers' is FALSE (or not set) the type 'integer' is treated
778
+ * exactly the same as 'number', and allows decimals.
779
+ *
780
+ * Valid Examples:
781
+ * toJavaScriptType('10', 'number' ) = 10 // '10' is a number
782
+ * toJavaScriptType('10', 'integer') = 10 // '10' is also an integer
783
+ * toJavaScriptType( 10, 'integer') = 10 // 10 is still an integer
784
+ * toJavaScriptType( 10, 'string' ) = '10' // 10 can be made into a string
785
+ * toJavaScriptType('10.5', 'number' ) = 10.5 // '10.5' is a number
786
+ *
787
+ * Invalid Examples:
788
+ * toJavaScriptType('10.5', 'integer') = null // '10.5' is not an integer
789
+ * toJavaScriptType( 10.5, 'integer') = null // 10.5 is still not an integer
790
+ *
791
+ * // { PrimitiveValue } value - value to convert
792
+ * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - types to convert to
793
+ * // { boolean = false } strictIntegers - if FALSE, treat integers as numbers
794
+ * // { PrimitiveValue }
850
795
  */
851
- function mergeFilteredObject(targetObject, sourceObject, excludeKeys = [], keyFn = (key) => key, valFn = (val) => val) {
852
- if (!isObject(sourceObject)) {
853
- return targetObject;
796
+ function toJavaScriptType(value, types, strictIntegers = true) {
797
+ if (!isDefined(value)) {
798
+ return null;
854
799
  }
855
- if (!isObject(targetObject)) {
856
- targetObject = {};
800
+ if (isString(types)) {
801
+ types = [types];
857
802
  }
858
- for (const key of Object.keys(sourceObject)) {
859
- if (!inArray(key, excludeKeys) && isDefined(sourceObject[key])) {
860
- targetObject[keyFn(key)] = valFn(sourceObject[key]);
803
+ if (strictIntegers && inArray('integer', types)) {
804
+ if (isInteger(value, 'strict')) {
805
+ return value;
806
+ }
807
+ if (isInteger(value)) {
808
+ return parseInt(value, 10);
861
809
  }
862
810
  }
863
- return targetObject;
864
- }
865
- /**
866
- * 'uniqueItems' function
867
- *
868
- * Accepts any number of string value inputs,
869
- * and returns an array of all input vaues, excluding duplicates.
870
- *
871
- * // {...string} ...items -
872
- * // {string[]} -
873
- */
874
- function uniqueItems(...items) {
875
- const returnItems = [];
876
- for (const item of items) {
877
- if (!returnItems.includes(item)) {
878
- returnItems.push(item);
811
+ if (inArray('number', types) || (!strictIntegers && inArray('integer', types))) {
812
+ if (isNumber(value, 'strict')) {
813
+ return value;
814
+ }
815
+ if (isNumber(value)) {
816
+ return parseFloat(value);
817
+ }
818
+ }
819
+ if (inArray('string', types)) {
820
+ if (isString(value)) {
821
+ return value;
822
+ }
823
+ // If value is a date, and types includes 'string',
824
+ // convert the date to a string
825
+ if (isDate(value)) {
826
+ return toIsoString(value);
827
+ }
828
+ if (isNumber(value)) {
829
+ return value.toString();
879
830
  }
880
831
  }
881
- return returnItems;
882
- }
883
- /**
884
- * 'commonItems' function
885
- *
886
- * Accepts any number of strings or arrays of string values,
887
- * and returns a single array containing only values present in all inputs.
888
- *
889
- * // {...string|string[]} ...arrays -
890
- * // {string[]} -
891
- */
892
- function commonItems(...arrays) {
893
- let returnItems = null;
894
- for (let array of arrays) {
895
- if (isString(array)) {
896
- array = [array];
832
+ // If value is a date, and types includes 'integer' or 'number',
833
+ // but not 'string', convert the date to a number
834
+ if (isDate(value) && (inArray('integer', types) || inArray('number', types))) {
835
+ return value.getTime();
836
+ }
837
+ if (inArray('boolean', types)) {
838
+ if (isBoolean(value, true)) {
839
+ return true;
897
840
  }
898
- returnItems = returnItems === null ? [...array] :
899
- returnItems.filter(item => array.includes(item));
900
- if (!returnItems.length) {
901
- return [];
841
+ if (isBoolean(value, false)) {
842
+ return false;
902
843
  }
903
844
  }
904
- return returnItems;
845
+ return null;
905
846
  }
906
847
  /**
907
- * 'fixTitle' function
908
- *
848
+ * 'toSchemaType' function
909
849
  *
910
- * // {string} input -
911
- * // {string} -
912
- */
913
- function fixTitle(name) {
914
- return name && toTitleCase(name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/_/g, ' '));
915
- }
916
- /**
917
- * 'toTitleCase' function
850
+ * Converts an input (probably string) value to the "best" JavaScript
851
+ * equivalent available from an allowed list of JSON Schema types, which may
852
+ * contain 'string', 'number', 'integer', 'boolean', and/or 'null'.
853
+ * If necssary, it does progressively agressive type coersion.
854
+ * It will not return null unless null is in the list of allowed types.
918
855
  *
919
- * Intelligently converts an input string to Title Case.
856
+ * Number conversion examples:
857
+ * toSchemaType('10', ['number','integer','string']) = 10 // integer
858
+ * toSchemaType('10', ['number','string']) = 10 // number
859
+ * toSchemaType('10', ['string']) = '10' // string
860
+ * toSchemaType('10.5', ['number','integer','string']) = 10.5 // number
861
+ * toSchemaType('10.5', ['integer','string']) = '10.5' // string
862
+ * toSchemaType('10.5', ['integer']) = 10 // integer
863
+ * toSchemaType(10.5, ['null','boolean','string']) = '10.5' // string
864
+ * toSchemaType(10.5, ['null','boolean']) = true // boolean
920
865
  *
921
- * Accepts an optional second parameter with a list of additional
922
- * words and abbreviations to force into a particular case.
866
+ * String conversion examples:
867
+ * toSchemaType('1.5x', ['boolean','number','integer','string']) = '1.5x' // string
868
+ * toSchemaType('1.5x', ['boolean','number','integer']) = '1.5' // number
869
+ * toSchemaType('1.5x', ['boolean','integer']) = '1' // integer
870
+ * toSchemaType('1.5x', ['boolean']) = true // boolean
871
+ * toSchemaType('xyz', ['number','integer','boolean','null']) = true // boolean
872
+ * toSchemaType('xyz', ['number','integer','null']) = null // null
873
+ * toSchemaType('xyz', ['number','integer']) = 0 // number
923
874
  *
924
- * This function is built on prior work by John Gruber and David Gouch:
925
- * http://daringfireball.net/2008/08/title_case_update
926
- * https://github.com/gouch/to-title-case
875
+ * Boolean conversion examples:
876
+ * toSchemaType('1', ['integer','number','string','boolean']) = 1 // integer
877
+ * toSchemaType('1', ['number','string','boolean']) = 1 // number
878
+ * toSchemaType('1', ['string','boolean']) = '1' // string
879
+ * toSchemaType('1', ['boolean']) = true // boolean
880
+ * toSchemaType('true', ['number','string','boolean']) = 'true' // string
881
+ * toSchemaType('true', ['boolean']) = true // boolean
882
+ * toSchemaType('true', ['number']) = 0 // number
883
+ * toSchemaType(true, ['number','string','boolean']) = true // boolean
884
+ * toSchemaType(true, ['number','string']) = 'true' // string
885
+ * toSchemaType(true, ['number']) = 1 // number
927
886
  *
928
- * // {string} input -
929
- * // {string|string[]} forceWords? -
930
- * // {string} -
887
+ * // { PrimitiveValue } value - value to convert
888
+ * // { SchemaPrimitiveType | SchemaPrimitiveType[] } types - allowed types to convert to
889
+ * // { PrimitiveValue }
931
890
  */
932
- function toTitleCase(input, forceWords) {
933
- if (!isString(input)) {
934
- return input;
891
+ function toSchemaType(value, types) {
892
+ if (!isArray(types)) {
893
+ types = [types];
935
894
  }
936
- let forceArray = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'en',
937
- 'for', 'if', 'in', 'nor', 'of', 'on', 'or', 'per', 'the', 'to', 'v', 'v.',
938
- 'vs', 'vs.', 'via'];
939
- if (isString(forceWords)) {
940
- forceWords = forceWords.split('|');
895
+ if (types.includes('null') && !hasValue(value)) {
896
+ return null;
941
897
  }
942
- if (isArray(forceWords)) {
943
- forceArray = forceArray.concat(forceWords);
898
+ if (types.includes('boolean') && !isBoolean(value, 'strict')) {
899
+ return value;
944
900
  }
945
- const forceArrayLower = forceArray.map(w => w.toLowerCase());
946
- const noInitialCase = input === input.toUpperCase() || input === input.toLowerCase();
947
- let prevLastChar = '';
948
- input = input.trim();
949
- return input.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, (word, idx) => {
950
- if (!noInitialCase && word.slice(1).search(/[A-Z]|\../) !== -1) {
951
- return word;
952
- }
953
- else {
954
- let newWord;
955
- const forceWord = forceArray[forceArrayLower.indexOf(word.toLowerCase())];
956
- if (!forceWord) {
957
- if (noInitialCase) {
958
- if (word.slice(1).search(/\../) !== -1) {
959
- newWord = word.toLowerCase();
960
- }
961
- else {
962
- newWord = word[0].toUpperCase() + word.slice(1).toLowerCase();
963
- }
964
- }
965
- else {
966
- newWord = word[0].toUpperCase() + word.slice(1);
967
- }
968
- }
969
- else if (forceWord === forceWord.toLowerCase() && (idx === 0 || idx + word.length === input.length ||
970
- prevLastChar === ':' || input[idx - 1].search(/[^\s-]/) !== -1 ||
971
- (input[idx - 1] !== '-' && input[idx + word.length] === '-'))) {
972
- newWord = forceWord[0].toUpperCase() + forceWord.slice(1);
973
- }
974
- else {
975
- newWord = forceWord;
976
- }
977
- prevLastChar = word.slice(-1);
978
- return newWord;
979
- }
980
- });
981
- }
982
-
983
- const deValidationMessages = {
984
- required: 'Darf nicht leer sein',
985
- minLength: 'Mindestens {{minimumLength}} Zeichen benötigt (aktuell: {{currentLength}})',
986
- maxLength: 'Maximal {{maximumLength}} Zeichen erlaubt (aktuell: {{currentLength}})',
987
- pattern: 'Entspricht nicht diesem regulären Ausdruck: {{requiredPattern}}',
988
- format: function (error) {
989
- switch (error.requiredFormat) {
990
- case 'date':
991
- return 'Muss ein Datum sein, z. B. "2000-12-31"';
992
- case 'time':
993
- return 'Muss eine Zeitangabe sein, z. B. "16:20" oder "03:14:15.9265"';
994
- case 'date-time':
995
- return 'Muss Datum mit Zeit beinhalten, z. B. "2000-03-14T01:59" oder "2000-03-14T01:59:26.535Z"';
996
- case 'email':
997
- return 'Keine gültige E-Mail-Adresse (z. B. "name@example.com")';
998
- case 'hostname':
999
- return 'Kein gültiger Hostname (z. B. "example.com")';
1000
- case 'ipv4':
1001
- return 'Keine gültige IPv4-Adresse (z. B. "127.0.0.1")';
1002
- case 'ipv6':
1003
- return 'Keine gültige IPv6-Adresse (z. B. "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0")';
1004
- // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
1005
- // case 'uri': case 'uri-reference': case 'uri-template':
1006
- case 'url':
1007
- return 'Keine gültige URL (z. B. "http://www.example.com/page.html")';
1008
- case 'uuid':
1009
- return 'Keine gültige UUID (z. B. "12345678-9ABC-DEF0-1234-56789ABCDEF0")';
1010
- case 'color':
1011
- return 'Kein gültiger Farbwert (z. B. "#FFFFFF" oder "rgb(255, 255, 255)")';
1012
- case 'json-pointer':
1013
- return 'Kein gültiger JSON-Pointer (z. B. "/pointer/to/something")';
1014
- case 'relative-json-pointer':
1015
- return 'Kein gültiger relativer JSON-Pointer (z. B. "2/pointer/to/something")';
1016
- case 'regex':
1017
- return 'Kein gültiger regulärer Ausdruck (z. B. "(1-)?\\d{3}-\\d{3}-\\d{4}")';
1018
- default:
1019
- return 'Muss diesem Format entsprechen: ' + error.requiredFormat;
1020
- }
1021
- },
1022
- minimum: 'Muss mindestens {{minimumValue}} sein',
1023
- exclusiveMinimum: 'Muss größer als {{exclusiveMinimumValue}} sein',
1024
- maximum: 'Darf maximal {{maximumValue}} sein',
1025
- exclusiveMaximum: 'Muss kleiner als {{exclusiveMaximumValue}} sein',
1026
- multipleOf: function (error) {
1027
- if ((1 / error.multipleOfValue) % 10 === 0) {
1028
- const decimals = Math.log10(1 / error.multipleOfValue);
1029
- return `Maximal ${decimals} Dezimalstellen erlaubt`;
1030
- }
1031
- else {
1032
- return `Muss ein Vielfaches von ${error.multipleOfValue} sein`;
901
+ if (types.includes('integer')) {
902
+ const testValue = toJavaScriptType(value, 'integer');
903
+ if (testValue !== null) {
904
+ return +testValue;
1033
905
  }
1034
- },
1035
- minProperties: 'Mindestens {{minimumProperties}} Attribute erforderlich (aktuell: {{currentProperties}})',
1036
- maxProperties: 'Maximal {{maximumProperties}} Attribute erlaubt (aktuell: {{currentProperties}})',
1037
- minItems: 'Mindestens {{minimumItems}} Werte erforderlich (aktuell: {{currentItems}})',
1038
- maxItems: 'Maximal {{maximumItems}} Werte erlaubt (aktuell: {{currentItems}})',
1039
- uniqueItems: 'Alle Werte müssen eindeutig sein',
1040
- // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
1041
- };
1042
-
1043
- const enValidationMessages = {
1044
- required: 'This field is required.',
1045
- minLength: 'Must be {{minimumLength}} characters or longer (current length: {{currentLength}})',
1046
- maxLength: 'Must be {{maximumLength}} characters or shorter (current length: {{currentLength}})',
1047
- pattern: 'Must match pattern: {{requiredPattern}}',
1048
- format: function (error) {
1049
- switch (error.requiredFormat) {
1050
- case 'date':
1051
- return 'Must be a date, like "2000-12-31"';
1052
- case 'time':
1053
- return 'Must be a time, like "16:20" or "03:14:15.9265"';
1054
- case 'date-time':
1055
- return 'Must be a date-time, like "2000-03-14T01:59" or "2000-03-14T01:59:26.535Z"';
1056
- case 'email':
1057
- return 'Must be an email address, like "name@example.com"';
1058
- case 'hostname':
1059
- return 'Must be a hostname, like "example.com"';
1060
- case 'ipv4':
1061
- return 'Must be an IPv4 address, like "127.0.0.1"';
1062
- case 'ipv6':
1063
- return 'Must be an IPv6 address, like "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
1064
- // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
1065
- // case 'uri': case 'uri-reference': case 'uri-template':
1066
- case 'url':
1067
- return 'Must be a url, like "http://www.example.com/page.html"';
1068
- case 'uuid':
1069
- return 'Must be a uuid, like "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
1070
- case 'color':
1071
- return 'Must be a color, like "#FFFFFF" or "rgb(255, 255, 255)"';
1072
- case 'json-pointer':
1073
- return 'Must be a JSON Pointer, like "/pointer/to/something"';
1074
- case 'relative-json-pointer':
1075
- return 'Must be a relative JSON Pointer, like "2/pointer/to/something"';
1076
- case 'regex':
1077
- return 'Must be a regular expression, like "(1-)?\\d{3}-\\d{3}-\\d{4}"';
1078
- default:
1079
- return 'Must be a correctly formatted ' + error.requiredFormat;
906
+ }
907
+ if (types.includes('number')) {
908
+ const testValue = toJavaScriptType(value, 'number');
909
+ if (testValue !== null) {
910
+ return +testValue;
1080
911
  }
1081
- },
1082
- minimum: 'Must be {{minimumValue}} or more',
1083
- exclusiveMinimum: 'Must be more than {{exclusiveMinimumValue}}',
1084
- maximum: 'Must be {{maximumValue}} or less',
1085
- exclusiveMaximum: 'Must be less than {{exclusiveMaximumValue}}',
1086
- multipleOf: function (error) {
1087
- if ((1 / error.multipleOfValue) % 10 === 0) {
1088
- const decimals = Math.log10(1 / error.multipleOfValue);
1089
- return `Must have ${decimals} or fewer decimal places.`;
912
+ }
913
+ if ((isString(value) || isNumber(value, 'strict')) &&
914
+ types.includes('string')) { // Convert number to string
915
+ return toJavaScriptType(value, 'string');
916
+ }
917
+ if (types.includes('boolean') && isBoolean(value)) {
918
+ return toJavaScriptType(value, 'boolean');
919
+ }
920
+ if (types.includes('string')) { // Convert null & boolean to string
921
+ if (value === null) {
922
+ return '';
1090
923
  }
1091
- else {
1092
- return `Must be a multiple of ${error.multipleOfValue}.`;
924
+ const testValue = toJavaScriptType(value, 'string');
925
+ if (testValue !== null) {
926
+ return testValue;
1093
927
  }
1094
- },
1095
- minProperties: 'Must have {{minimumProperties}} or more items (current items: {{currentProperties}})',
1096
- maxProperties: 'Must have {{maximumProperties}} or fewer items (current items: {{currentProperties}})',
1097
- minItems: 'Must have {{minimumItems}} or more items (current items: {{currentItems}})',
1098
- maxItems: 'Must have {{maximumItems}} or fewer items (current items: {{currentItems}})',
1099
- uniqueItems: 'All items must be unique',
1100
- // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
1101
- };
1102
-
1103
- const esValidationMessages = {
1104
- required: 'Este campo está requerido.',
1105
- minLength: 'Debe tener {{minimumLength}} caracteres o más longitud (longitud actual: {{currentLength}})',
1106
- maxLength: 'Debe tener {{maximumLength}} caracteres o menos longitud (longitud actual: {{currentLength}})',
1107
- pattern: 'Must match pattern: {{requiredPattern}}',
1108
- format: function (error) {
1109
- switch (error.requiredFormat) {
1110
- case 'date':
1111
- return 'Debe tener una fecha, ej "2000-12-31"';
1112
- case 'time':
1113
- return 'Debe tener una hora, ej "16:20" o "03:14:15.9265"';
1114
- case 'date-time':
1115
- return 'Debe tener fecha y hora, ej "2000-03-14T01:59" o "2000-03-14T01:59:26.535Z"';
1116
- case 'email':
1117
- return 'No hay dirección de correo electrónico válida, ej "name@example.com"';
1118
- case 'hostname':
1119
- return 'Debe ser un nombre de host válido, ej "example.com"';
1120
- case 'ipv4':
1121
- return 'Debe ser una dirección de IPv4, ej "127.0.0.1"';
1122
- case 'ipv6':
1123
- return 'Debe ser una dirección de IPv6, ej "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
1124
- case 'url':
1125
- return 'Debe ser una URL, ej "http://www.example.com/page.html"';
1126
- case 'uuid':
1127
- return 'Debe ser un UUID, ej "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
1128
- case 'color':
1129
- return 'Debe ser un color, ej "#FFFFFF" or "rgb(255, 255, 255)"';
1130
- case 'json-pointer':
1131
- return 'Debe ser un JSON Pointer, ej "/pointer/to/something"';
1132
- case 'relative-json-pointer':
1133
- return 'Debe ser un JSON Pointer relativo, ej "2/pointer/to/something"';
1134
- case 'regex':
1135
- return 'Debe ser una expresión regular, ej "(1-)?\\d{3}-\\d{3}-\\d{4}"';
1136
- default:
1137
- return 'Debe tener el formato correcto ' + error.requiredFormat;
928
+ }
929
+ if ((types.includes('number') ||
930
+ types.includes('integer'))) {
931
+ if (value === true) {
932
+ return 1;
933
+ } // Convert boolean & null to number
934
+ if (value === false || value === null || value === '') {
935
+ return 0;
1138
936
  }
1139
- },
1140
- minimum: 'Debe ser {{minimumValue}} o más',
1141
- exclusiveMinimum: 'Debe ser superior a {{exclusiveMinimumValue}}',
1142
- maximum: 'Debe ser {{maximumValue}} o menos',
1143
- exclusiveMaximum: 'Debe ser menor que {{exclusiveMaximumValue}}',
1144
- multipleOf: function (error) {
1145
- if ((1 / error.multipleOfValue) % 10 === 0) {
1146
- const decimals = Math.log10(1 / error.multipleOfValue);
1147
- return `Se permite un máximo de ${decimals} decimales`;
937
+ }
938
+ if (types.includes('number')) { // Convert mixed string to number
939
+ const testValue = parseFloat(value);
940
+ if (!!testValue) {
941
+ return testValue;
1148
942
  }
1149
- else {
1150
- return `Debe ser múltiplo de ${error.multipleOfValue}.`;
943
+ }
944
+ if (types.includes('integer')) { // Convert string or number to integer
945
+ const testValue = parseInt(value, 10);
946
+ if (!!testValue) {
947
+ return testValue;
1151
948
  }
1152
- },
1153
- minProperties: 'Debe tener {{minimumProperties}} o más elementos (elementos actuales: {{currentProperties}})',
1154
- maxProperties: 'Debe tener {{maximumProperties}} o menos elementos (elementos actuales: {{currentProperties}})',
1155
- minItems: 'Debe tener {{minimumItems}} o más elementos (elementos actuales: {{currentItems}})',
1156
- maxItems: 'Debe tener {{maximumItems}} o menos elementos (elementos actuales: {{currentItems}})',
1157
- uniqueItems: 'Todos los elementos deben ser únicos',
1158
- };
949
+ }
950
+ if (types.includes('boolean')) { // Convert anything to boolean
951
+ return !!value;
952
+ }
953
+ if ((types.includes('number') ||
954
+ types.includes('integer')) && !types.includes('null')) {
955
+ return 0; // If null not allowed, return 0 for non-convertable values
956
+ }
957
+ }
958
+ /**
959
+ * 'isPromise' function
960
+ *
961
+ * // object
962
+ * // { boolean }
963
+ */
964
+ function isPromise(object) {
965
+ return !!object && typeof object.then === 'function';
966
+ }
967
+ /**
968
+ * 'isObservable' function
969
+ *
970
+ * // object
971
+ * // { boolean }
972
+ */
973
+ function isObservable(object) {
974
+ return !!object && typeof object.subscribe === 'function';
975
+ }
976
+ /**
977
+ * '_toPromise' function
978
+ *
979
+ * // { object } object
980
+ * // { Promise<any> }
981
+ */
982
+ function _toPromise(object) {
983
+ return isPromise(object) ? object : object.toPromise();
984
+ }
985
+ /**
986
+ * 'toObservable' function
987
+ *
988
+ * // { object } object
989
+ * // { Observable<any> }
990
+ */
991
+ function toObservable(object) {
992
+ const observable = isPromise(object) ? from(object) : object;
993
+ if (isObservable(observable)) {
994
+ return observable;
995
+ }
996
+ console.error('toObservable error: Expected validator to return Promise or Observable.');
997
+ return new Observable();
998
+ }
999
+ /**
1000
+ * 'inArray' function
1001
+ *
1002
+ * Searches an array for an item, or one of a list of items, and returns true
1003
+ * as soon as a match is found, or false if no match.
1004
+ *
1005
+ * If the optional third parameter allIn is set to TRUE, and the item to find
1006
+ * is an array, then the function returns true only if all elements from item
1007
+ * are found in the array list, and false if any element is not found. If the
1008
+ * item to find is not an array, setting allIn to TRUE has no effect.
1009
+ *
1010
+ * // { any|any[] } item - the item to search for
1011
+ * // array - the array to search
1012
+ * // { boolean = false } allIn - if TRUE, all items must be in array
1013
+ * // { boolean } - true if item(s) in array, false otherwise
1014
+ */
1015
+ function inArray(item, array, allIn = false) {
1016
+ if (!isDefined(item) || !isArray(array)) {
1017
+ return false;
1018
+ }
1019
+ return isArray(item) ?
1020
+ item[allIn ? 'every' : 'some'](subItem => array.includes(subItem)) :
1021
+ array.includes(item);
1022
+ }
1023
+ /**
1024
+ * 'xor' utility function - exclusive or
1025
+ *
1026
+ * Returns true if exactly one of two values is truthy.
1027
+ *
1028
+ * // value1 - first value to check
1029
+ * // value2 - second value to check
1030
+ * // { boolean } - true if exactly one input value is truthy, false if not
1031
+ */
1032
+ function xor(value1, value2) {
1033
+ return (!!value1 && !value2) || (!value1 && !!value2);
1034
+ }
1159
1035
 
1160
- const frValidationMessages = {
1161
- required: 'Est obligatoire.',
1162
- minLength: 'Doit avoir minimum {{minimumLength}} caractères (actuellement: {{currentLength}})',
1163
- maxLength: 'Doit avoir maximum {{maximumLength}} caractères (actuellement: {{currentLength}})',
1164
- pattern: 'Doit respecter: {{requiredPattern}}',
1165
- format: function (error) {
1166
- switch (error.requiredFormat) {
1167
- case 'date':
1168
- return 'Doit être une date, tel que "2000-12-31"';
1169
- case 'time':
1170
- return 'Doit être une heure, tel que "16:20" ou "03:14:15.9265"';
1171
- case 'date-time':
1172
- return 'Doit être une date et une heure, tel que "2000-03-14T01:59" ou "2000-03-14T01:59:26.535Z"';
1173
- case 'email':
1174
- return 'Doit être une adresse e-mail, tel que "name@example.com"';
1175
- case 'hostname':
1176
- return 'Doit être un nom de domaine, tel que "example.com"';
1177
- case 'ipv4':
1178
- return 'Doit être une adresse IPv4, tel que "127.0.0.1"';
1179
- case 'ipv6':
1180
- return 'Doit être une adresse IPv6, tel que "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
1181
- // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
1182
- // case 'uri': case 'uri-reference': case 'uri-template':
1183
- case 'url':
1184
- return 'Doit être une URL, tel que "http://www.example.com/page.html"';
1185
- case 'uuid':
1186
- return 'Doit être un UUID, tel que "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
1187
- case 'color':
1188
- return 'Doit être une couleur, tel que "#FFFFFF" or "rgb(255, 255, 255)"';
1189
- case 'json-pointer':
1190
- return 'Doit être un JSON Pointer, tel que "/pointer/to/something"';
1191
- case 'relative-json-pointer':
1192
- return 'Doit être un relative JSON Pointer, tel que "2/pointer/to/something"';
1193
- case 'regex':
1194
- return 'Doit être une expression régulière, tel que "(1-)?\\d{3}-\\d{3}-\\d{4}"';
1195
- default:
1196
- return 'Doit être avoir le format correct: ' + error.requiredFormat;
1036
+ /**
1037
+ * Utility function library:
1038
+ *
1039
+ * addClasses, copy, forEach, forEachCopy, hasOwn, mergeFilteredObject,
1040
+ * uniqueItems, commonItems, fixTitle, toTitleCase
1041
+ */
1042
+ /**
1043
+ * 'addClasses' function
1044
+ *
1045
+ * Merges two space-delimited lists of CSS classes and removes duplicates.
1046
+ *
1047
+ * // {string | string[] | Set<string>} oldClasses
1048
+ * // {string | string[] | Set<string>} newClasses
1049
+ * // {string | string[] | Set<string>} - Combined classes
1050
+ */
1051
+ function addClasses(oldClasses, newClasses) {
1052
+ const badType = i => !isSet(i) && !isArray(i) && !isString(i);
1053
+ if (badType(newClasses)) {
1054
+ return oldClasses;
1055
+ }
1056
+ if (badType(oldClasses)) {
1057
+ oldClasses = '';
1058
+ }
1059
+ const toSet = i => isSet(i) ? i : isArray(i) ? new Set(i) : new Set(i.split(' '));
1060
+ const combinedSet = toSet(oldClasses);
1061
+ const newSet = toSet(newClasses);
1062
+ newSet.forEach(c => combinedSet.add(c));
1063
+ if (isSet(oldClasses)) {
1064
+ return combinedSet;
1065
+ }
1066
+ if (isArray(oldClasses)) {
1067
+ return Array.from(combinedSet);
1068
+ }
1069
+ return Array.from(combinedSet).join(' ');
1070
+ }
1071
+ /**
1072
+ * 'copy' function
1073
+ *
1074
+ * Makes a shallow copy of a JavaScript object, array, Map, or Set.
1075
+ * If passed a JavaScript primitive value (string, number, boolean, or null),
1076
+ * it returns the value.
1077
+ *
1078
+ * // {Object|Array|string|number|boolean|null} object - The object to copy
1079
+ * // {boolean = false} errors - Show errors?
1080
+ * // {Object|Array|string|number|boolean|null} - The copied object
1081
+ */
1082
+ function copy(object, errors = false) {
1083
+ if (typeof object !== 'object' || object === null) {
1084
+ return object;
1085
+ }
1086
+ if (isMap(object)) {
1087
+ return new Map(object);
1088
+ }
1089
+ if (isSet(object)) {
1090
+ return new Set(object);
1091
+ }
1092
+ if (isArray(object)) {
1093
+ return [...object];
1094
+ }
1095
+ if (isObject(object)) {
1096
+ return { ...object };
1097
+ }
1098
+ if (errors) {
1099
+ console.error('copy error: Object to copy must be a JavaScript object or value.');
1100
+ }
1101
+ return object;
1102
+ }
1103
+ /**
1104
+ * 'forEach' function
1105
+ *
1106
+ * Iterates over all items in the first level of an object or array
1107
+ * and calls an iterator funciton on each item.
1108
+ *
1109
+ * The iterator function is called with four values:
1110
+ * 1. The current item's value
1111
+ * 2. The current item's key
1112
+ * 3. The parent object, which contains the current item
1113
+ * 4. The root object
1114
+ *
1115
+ * Setting the optional third parameter to 'top-down' or 'bottom-up' will cause
1116
+ * it to also recursively iterate over items in sub-objects or sub-arrays in the
1117
+ * specified direction.
1118
+ *
1119
+ * // {Object|Array} object - The object or array to iterate over
1120
+ * // {function} fn - the iterator funciton to call on each item
1121
+ * // {boolean = false} errors - Show errors?
1122
+ * // {void}
1123
+ */
1124
+ function forEach(object, fn, recurse = false, rootObject = object, errors = false) {
1125
+ if (isEmpty(object)) {
1126
+ return;
1127
+ }
1128
+ if ((isObject(object) || isArray(object)) && typeof fn === 'function') {
1129
+ for (const key of Object.keys(object)) {
1130
+ const value = object[key];
1131
+ if (recurse === 'bottom-up' && (isObject(value) || isArray(value))) {
1132
+ forEach(value, fn, recurse, rootObject);
1133
+ }
1134
+ fn(value, key, object, rootObject);
1135
+ if (recurse === 'top-down' && (isObject(value) || isArray(value))) {
1136
+ forEach(value, fn, recurse, rootObject);
1137
+ }
1197
1138
  }
1198
- },
1199
- minimum: 'Doit être supérieur à {{minimumValue}}',
1200
- exclusiveMinimum: 'Doit avoir minimum {{exclusiveMinimumValue}} charactères',
1201
- maximum: 'Doit être inférieur à {{maximumValue}}',
1202
- exclusiveMaximum: 'Doit avoir maximum {{exclusiveMaximumValue}} charactères',
1203
- multipleOf: function (error) {
1204
- if ((1 / error.multipleOfValue) % 10 === 0) {
1205
- const decimals = Math.log10(1 / error.multipleOfValue);
1206
- return `Doit comporter ${decimals} ou moins de decimales.`;
1139
+ }
1140
+ if (errors) {
1141
+ if (typeof fn !== 'function') {
1142
+ console.error('forEach error: Iterator must be a function.');
1143
+ console.error('function', fn);
1207
1144
  }
1208
- else {
1209
- return `Doit être un multiple de ${error.multipleOfValue}.`;
1145
+ if (!isObject(object) && !isArray(object)) {
1146
+ console.error('forEach error: Input object must be an object or array.');
1147
+ console.error('object', object);
1210
1148
  }
1211
- },
1212
- minProperties: 'Doit comporter au minimum {{minimumProperties}} éléments',
1213
- maxProperties: 'Doit comporter au maximum {{maximumProperties}} éléments',
1214
- minItems: 'Doit comporter au minimum {{minimumItems}} éléments',
1215
- maxItems: 'Doit comporter au maximum {{minimumItems}} éléments',
1216
- uniqueItems: 'Tous les éléments doivent être uniques',
1217
- // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
1218
- };
1219
-
1220
- const itValidationMessages = {
1221
- required: 'Il campo è obbligatorio',
1222
- minLength: 'Deve inserire almeno {{minimumLength}} caratteri (lunghezza corrente: {{currentLength}})',
1223
- maxLength: 'Il numero massimo di caratteri consentito è {{maximumLength}} (lunghezza corrente: {{currentLength}})',
1224
- pattern: 'Devi rispettare il pattern : {{requiredPattern}}',
1225
- format: function (error) {
1226
- switch (error.requiredFormat) {
1227
- case 'date':
1228
- return 'Deve essere una data, come "31-12-2000"';
1229
- case 'time':
1230
- return 'Deve essere un orario, come "16:20" o "03:14:15.9265"';
1231
- case 'date-time':
1232
- return 'Deve essere data-orario, come "14-03-2000T01:59" or "14-03-2000T01:59:26.535Z"';
1233
- case 'email':
1234
- return 'Deve essere un indirzzo email, come "name@example.com"';
1235
- case 'hostname':
1236
- return 'Deve essere un hostname, come "example.com"';
1237
- case 'ipv4':
1238
- return 'Deve essere un indirizzo IPv4, come "127.0.0.1"';
1239
- case 'ipv6':
1240
- return 'Deve essere un indirizzo IPv6, come "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
1241
- // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
1242
- // case 'uri': case 'uri-reference': case 'uri-template':
1243
- case 'url':
1244
- return 'Deve essere un url, come "http://www.example.com/page.html"';
1245
- case 'uuid':
1246
- return 'Deve essere un uuid, come "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
1247
- case 'color':
1248
- return 'Deve essere un colore, come "#FFFFFF" o "rgb(255, 255, 255)"';
1249
- case 'json-pointer':
1250
- return 'Deve essere un JSON Pointer, come "/pointer/to/something"';
1251
- case 'relative-json-pointer':
1252
- return 'Deve essere un JSON Pointer relativo, come "2/pointer/to/something"';
1253
- case 'regex':
1254
- return 'Deve essere una regular expression, come "(1-)?\\d{3}-\\d{3}-\\d{4}"';
1255
- default:
1256
- return 'Deve essere formattato correttamente ' + error.requiredFormat;
1149
+ }
1150
+ }
1151
+ /**
1152
+ * 'forEachCopy' function
1153
+ *
1154
+ * Iterates over all items in the first level of an object or array
1155
+ * and calls an iterator function on each item. Returns a new object or array
1156
+ * with the same keys or indexes as the original, and values set to the results
1157
+ * of the iterator function.
1158
+ *
1159
+ * Does NOT recursively iterate over items in sub-objects or sub-arrays.
1160
+ *
1161
+ * // {Object | Array} object - The object or array to iterate over
1162
+ * // {function} fn - The iterator funciton to call on each item
1163
+ * // {boolean = false} errors - Show errors?
1164
+ * // {Object | Array} - The resulting object or array
1165
+ */
1166
+ function forEachCopy(object, fn, errors = false) {
1167
+ if (!hasValue(object)) {
1168
+ return;
1169
+ }
1170
+ if ((isObject(object) || isArray(object)) && typeof object !== 'function') {
1171
+ const newObject = isArray(object) ? [] : {};
1172
+ for (const key of Object.keys(object)) {
1173
+ newObject[key] = fn(object[key], key, object);
1257
1174
  }
1258
- },
1259
- minimum: 'Deve essere {{minimumValue}} o più',
1260
- exclusiveMinimum: 'Deve essere più di {{exclusiveMinimumValue}}',
1261
- maximum: 'Deve essere {{maximumValue}} o meno',
1262
- exclusiveMaximum: 'Deve essere minore di {{exclusiveMaximumValue}}',
1263
- multipleOf: function (error) {
1264
- if ((1 / error.multipleOfValue) % 10 === 0) {
1265
- const decimals = Math.log10(1 / error.multipleOfValue);
1266
- return `Deve avere ${decimals} o meno decimali.`;
1175
+ return newObject;
1176
+ }
1177
+ if (errors) {
1178
+ if (typeof fn !== 'function') {
1179
+ console.error('forEachCopy error: Iterator must be a function.');
1180
+ console.error('function', fn);
1267
1181
  }
1268
- else {
1269
- return `Deve essere multiplo di ${error.multipleOfValue}.`;
1182
+ if (!isObject(object) && !isArray(object)) {
1183
+ console.error('forEachCopy error: Input object must be an object or array.');
1184
+ console.error('object', object);
1270
1185
  }
1271
- },
1272
- minProperties: 'Deve avere {{minimumProperties}} o più elementi (elementi correnti: {{currentProperties}})',
1273
- maxProperties: 'Deve avere {{maximumProperties}} o meno elementi (elementi correnti: {{currentProperties}})',
1274
- minItems: 'Deve avere {{minimumItems}} o più elementi (elementi correnti: {{currentItems}})',
1275
- maxItems: 'Deve avere {{maximumItems}} o meno elementi (elementi correnti: {{currentItems}})',
1276
- uniqueItems: 'Tutti gli elementi devono essere unici',
1277
- // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
1278
- };
1279
-
1280
- const ptValidationMessages = {
1281
- required: 'Este campo é obrigatório.',
1282
- minLength: 'É preciso no mínimo {{minimumLength}} caracteres ou mais (tamanho atual: {{currentLength}})',
1283
- maxLength: preciso no máximo {{maximumLength}} caracteres ou menos (tamanho atual: {{currentLength}})',
1284
- pattern: 'Tem que ajustar ao formato: {{requiredPattern}}',
1285
- format: function (error) {
1286
- switch (error.requiredFormat) {
1287
- case 'date':
1288
- return 'Tem que ser uma data, por exemplo "2000-12-31"';
1289
- case 'time':
1290
- return 'Tem que ser horário, por exemplo "16:20" ou "03:14:15.9265"';
1291
- case 'date-time':
1292
- return 'Tem que ser data e hora, por exemplo "2000-03-14T01:59" ou "2000-03-14T01:59:26.535Z"';
1293
- case 'email':
1294
- return 'Tem que ser um email, por exemplo "fulano@exemplo.com.br"';
1295
- case 'hostname':
1296
- return 'Tem que ser uma nome de domínio, por exemplo "exemplo.com.br"';
1297
- case 'ipv4':
1298
- return 'Tem que ser um endereço IPv4, por exemplo "127.0.0.1"';
1299
- case 'ipv6':
1300
- return 'Tem que ser um endereço IPv6, por exemplo "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
1301
- // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
1302
- // case 'uri': case 'uri-reference': case 'uri-template':
1303
- case 'url':
1304
- return 'Tem que ser uma URL, por exemplo "http://www.exemplo.com.br/pagina.html"';
1305
- case 'uuid':
1306
- return 'Tem que ser um uuid, por exemplo "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
1307
- case 'color':
1308
- return 'Tem que ser uma cor, por exemplo "#FFFFFF" ou "rgb(255, 255, 255)"';
1309
- case 'json-pointer':
1310
- return 'Tem que ser um JSON Pointer, por exemplo "/referencia/para/algo"';
1311
- case 'relative-json-pointer':
1312
- return 'Tem que ser um JSON Pointer relativo, por exemplo "2/referencia/para/algo"';
1313
- case 'regex':
1314
- return 'Tem que ser uma expressão regular, por exemplo "(1-)?\\d{3}-\\d{3}-\\d{4}"';
1315
- default:
1316
- return 'Tem que ser no formato: ' + error.requiredFormat;
1186
+ }
1187
+ }
1188
+ /**
1189
+ * 'hasOwn' utility function
1190
+ *
1191
+ * Checks whether an object or array has a particular property.
1192
+ *
1193
+ * // {any} object - the object to check
1194
+ * // {string} property - the property to look for
1195
+ * // {boolean} - true if object has property, false if not
1196
+ */
1197
+ function hasOwn(object, property) {
1198
+ if (!object || !['number', 'string', 'symbol'].includes(typeof property) ||
1199
+ (!isObject(object) && !isArray(object) && !isMap(object) && !isSet(object))) {
1200
+ return false;
1201
+ }
1202
+ if (isMap(object) || isSet(object)) {
1203
+ return object.has(property);
1204
+ }
1205
+ if (typeof property === 'number') {
1206
+ if (isArray(object)) {
1207
+ return object[property];
1317
1208
  }
1318
- },
1319
- minimum: 'Tem que ser {{minimumValue}} ou mais',
1320
- exclusiveMinimum: 'Tem que ser mais que {{exclusiveMinimumValue}}',
1321
- maximum: 'Tem que ser {{maximumValue}} ou menos',
1322
- exclusiveMaximum: 'Tem que ser menor que {{exclusiveMaximumValue}}',
1323
- multipleOf: function (error) {
1324
- if ((1 / error.multipleOfValue) % 10 === 0) {
1325
- const decimals = Math.log10(1 / error.multipleOfValue);
1326
- return `Tem que ter ${decimals} ou menos casas decimais.`;
1209
+ property = property + '';
1210
+ }
1211
+ return object.hasOwnProperty(property);
1212
+ }
1213
+ /**
1214
+ * Types of possible expressions which the app is able to evaluate.
1215
+ */
1216
+ var ExpressionType;
1217
+ (function (ExpressionType) {
1218
+ ExpressionType[ExpressionType["EQUALS"] = 0] = "EQUALS";
1219
+ ExpressionType[ExpressionType["NOT_EQUALS"] = 1] = "NOT_EQUALS";
1220
+ ExpressionType[ExpressionType["NOT_AN_EXPRESSION"] = 2] = "NOT_AN_EXPRESSION";
1221
+ })(ExpressionType || (ExpressionType = {}));
1222
+ /**
1223
+ * Detects the type of expression from the given candidate. `==` for equals,
1224
+ * `!=` for not equals. If none of these are contained in the candidate, the candidate
1225
+ * is not considered to be an expression at all and thus `NOT_AN_EXPRESSION` is returned.
1226
+ * // {expressionCandidate} expressionCandidate - potential expression
1227
+ */
1228
+ function getExpressionType(expressionCandidate) {
1229
+ if (expressionCandidate.indexOf('==') !== -1) {
1230
+ return ExpressionType.EQUALS;
1231
+ }
1232
+ if (expressionCandidate.toString().indexOf('!=') !== -1) {
1233
+ return ExpressionType.NOT_EQUALS;
1234
+ }
1235
+ return ExpressionType.NOT_AN_EXPRESSION;
1236
+ }
1237
+ function isEqual(expressionType) {
1238
+ return expressionType === ExpressionType.EQUALS;
1239
+ }
1240
+ function isNotEqual(expressionType) {
1241
+ return expressionType === ExpressionType.NOT_EQUALS;
1242
+ }
1243
+ function isNotExpression(expressionType) {
1244
+ return expressionType === ExpressionType.NOT_AN_EXPRESSION;
1245
+ }
1246
+ /**
1247
+ * Splits the expression key by the expressionType on a pair of values
1248
+ * before and after the equals or nor equals sign.
1249
+ * // {expressionType} enum of an expression type
1250
+ * // {key} the given key from a for loop iver all conditions
1251
+ */
1252
+ function getKeyAndValueByExpressionType(expressionType, key) {
1253
+ if (isEqual(expressionType)) {
1254
+ return key.split('==', 2);
1255
+ }
1256
+ if (isNotEqual(expressionType)) {
1257
+ return key.split('!=', 2);
1258
+ }
1259
+ return null;
1260
+ }
1261
+ function cleanValueOfQuotes(keyAndValue) {
1262
+ if (keyAndValue.charAt(0) === '\'' && keyAndValue.charAt(keyAndValue.length - 1) === '\'') {
1263
+ return keyAndValue.replace('\'', '').replace('\'', '');
1264
+ }
1265
+ return keyAndValue;
1266
+ }
1267
+ /**
1268
+ * 'mergeFilteredObject' utility function
1269
+ *
1270
+ * Shallowly merges two objects, setting key and values from source object
1271
+ * in target object, excluding specified keys.
1272
+ *
1273
+ * Optionally, it can also use functions to transform the key names and/or
1274
+ * the values of the merging object.
1275
+ *
1276
+ * // {PlainObject} targetObject - Target object to add keys and values to
1277
+ * // {PlainObject} sourceObject - Source object to copy keys and values from
1278
+ * // {string[]} excludeKeys - Array of keys to exclude
1279
+ * // {(string: string) => string = (k) => k} keyFn - Function to apply to keys
1280
+ * // {(any: any) => any = (v) => v} valueFn - Function to apply to values
1281
+ * // {PlainObject} - Returns targetObject
1282
+ */
1283
+ function mergeFilteredObject(targetObject, sourceObject, excludeKeys = [], keyFn = (key) => key, valFn = (val) => val) {
1284
+ if (!isObject(sourceObject)) {
1285
+ return targetObject;
1286
+ }
1287
+ if (!isObject(targetObject)) {
1288
+ targetObject = {};
1289
+ }
1290
+ for (const key of Object.keys(sourceObject)) {
1291
+ if (!inArray(key, excludeKeys) && isDefined(sourceObject[key])) {
1292
+ targetObject[keyFn(key)] = valFn(sourceObject[key]);
1327
1293
  }
1328
- else {
1329
- return `Tem que ser um múltiplo de ${error.multipleOfValue}.`;
1294
+ }
1295
+ return targetObject;
1296
+ }
1297
+ /**
1298
+ * 'uniqueItems' function
1299
+ *
1300
+ * Accepts any number of string value inputs,
1301
+ * and returns an array of all input vaues, excluding duplicates.
1302
+ *
1303
+ * // {...string} ...items -
1304
+ * // {string[]} -
1305
+ */
1306
+ function uniqueItems(...items) {
1307
+ const returnItems = [];
1308
+ for (const item of items) {
1309
+ if (!returnItems.includes(item)) {
1310
+ returnItems.push(item);
1330
1311
  }
1331
- },
1332
- minProperties: 'Deve ter {{minimumProperties}} ou mais itens (itens até o momento: {{currentProperties}})',
1333
- maxProperties: 'Deve ter {{maximumProperties}} ou menos intens (itens até o momento: {{currentProperties}})',
1334
- minItems: 'Deve ter {{minimumItems}} ou mais itens (itens até o momento: {{currentItems}})',
1335
- maxItems: 'Deve ter {{maximumItems}} ou menos itens (itens até o momento: {{currentItems}})',
1336
- uniqueItems: 'Todos os itens devem ser únicos',
1337
- // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
1338
- };
1339
-
1340
- const zhValidationMessages = {
1341
- required: '必填字段.',
1342
- minLength: '字符长度必须大于或者等于 {{minimumLength}} (当前长度: {{currentLength}})',
1343
- maxLength: '字符长度必须小于或者等于 {{maximumLength}} (当前长度: {{currentLength}})',
1344
- pattern: '必须匹配正则表达式: {{requiredPattern}}',
1345
- format: function (error) {
1346
- switch (error.requiredFormat) {
1347
- case 'date':
1348
- return '必须为日期格式, 比如 "2000-12-31"';
1349
- case 'time':
1350
- return '必须为时间格式, 比如 "16:20" 或者 "03:14:15.9265"';
1351
- case 'date-time':
1352
- return '必须为日期时间格式, 比如 "2000-03-14T01:59" 或者 "2000-03-14T01:59:26.535Z"';
1353
- case 'email':
1354
- return '必须为邮箱地址, 比如 "name@example.com"';
1355
- case 'hostname':
1356
- return '必须为主机名, 比如 "example.com"';
1357
- case 'ipv4':
1358
- return '必须为 IPv4 地址, 比如 "127.0.0.1"';
1359
- case 'ipv6':
1360
- return '必须为 IPv6 地址, 比如 "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0"';
1361
- // TODO: add examples for 'uri', 'uri-reference', and 'uri-template'
1362
- // case 'uri': case 'uri-reference': case 'uri-template':
1363
- case 'url':
1364
- return '必须为 url, 比如 "http://www.example.com/page.html"';
1365
- case 'uuid':
1366
- return '必须为 uuid, 比如 "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
1367
- case 'color':
1368
- return '必须为颜色值, 比如 "#FFFFFF" 或者 "rgb(255, 255, 255)"';
1369
- case 'json-pointer':
1370
- return '必须为 JSON Pointer, 比如 "/pointer/to/something"';
1371
- case 'relative-json-pointer':
1372
- return '必须为相对的 JSON Pointer, 比如 "2/pointer/to/something"';
1373
- case 'regex':
1374
- return '必须为正则表达式, 比如 "(1-)?\\d{3}-\\d{3}-\\d{4}"';
1375
- default:
1376
- return '必须为格式正确的 ' + error.requiredFormat;
1312
+ }
1313
+ return returnItems;
1314
+ }
1315
+ /**
1316
+ * 'commonItems' function
1317
+ *
1318
+ * Accepts any number of strings or arrays of string values,
1319
+ * and returns a single array containing only values present in all inputs.
1320
+ *
1321
+ * // {...string|string[]} ...arrays -
1322
+ * // {string[]} -
1323
+ */
1324
+ function commonItems(...arrays) {
1325
+ let returnItems = null;
1326
+ for (let array of arrays) {
1327
+ if (isString(array)) {
1328
+ array = [array];
1377
1329
  }
1378
- },
1379
- minimum: '必须大于或者等于最小值: {{minimumValue}}',
1380
- exclusiveMinimum: '必须大于最小值: {{exclusiveMinimumValue}}',
1381
- maximum: '必须小于或者等于最大值: {{maximumValue}}',
1382
- exclusiveMaximum: '必须小于最大值: {{exclusiveMaximumValue}}',
1383
- multipleOf: function (error) {
1384
- if ((1 / error.multipleOfValue) % 10 === 0) {
1385
- const decimals = Math.log10(1 / error.multipleOfValue);
1386
- return `必须有 ${decimals} 位或更少的小数位`;
1330
+ returnItems = returnItems === null ? [...array] :
1331
+ returnItems.filter(item => array.includes(item));
1332
+ if (!returnItems.length) {
1333
+ return [];
1334
+ }
1335
+ }
1336
+ return returnItems;
1337
+ }
1338
+ /**
1339
+ * 'fixTitle' function
1340
+ *
1341
+ *
1342
+ * // {string} input -
1343
+ * // {string} -
1344
+ */
1345
+ function fixTitle(name) {
1346
+ return name && toTitleCase(name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/_/g, ' '));
1347
+ }
1348
+ /**
1349
+ * 'toTitleCase' function
1350
+ *
1351
+ * Intelligently converts an input string to Title Case.
1352
+ *
1353
+ * Accepts an optional second parameter with a list of additional
1354
+ * words and abbreviations to force into a particular case.
1355
+ *
1356
+ * This function is built on prior work by John Gruber and David Gouch:
1357
+ * http://daringfireball.net/2008/08/title_case_update
1358
+ * https://github.com/gouch/to-title-case
1359
+ *
1360
+ * // {string} input -
1361
+ * // {string|string[]} forceWords? -
1362
+ * // {string} -
1363
+ */
1364
+ function toTitleCase(input, forceWords) {
1365
+ if (!isString(input)) {
1366
+ return input;
1367
+ }
1368
+ let forceArray = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'en',
1369
+ 'for', 'if', 'in', 'nor', 'of', 'on', 'or', 'per', 'the', 'to', 'v', 'v.',
1370
+ 'vs', 'vs.', 'via'];
1371
+ if (isString(forceWords)) {
1372
+ forceWords = forceWords.split('|');
1373
+ }
1374
+ if (isArray(forceWords)) {
1375
+ forceArray = forceArray.concat(forceWords);
1376
+ }
1377
+ const forceArrayLower = forceArray.map(w => w.toLowerCase());
1378
+ const noInitialCase = input === input.toUpperCase() || input === input.toLowerCase();
1379
+ let prevLastChar = '';
1380
+ input = input.trim();
1381
+ return input.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, (word, idx) => {
1382
+ if (!noInitialCase && word.slice(1).search(/[A-Z]|\../) !== -1) {
1383
+ return word;
1387
1384
  }
1388
1385
  else {
1389
- return `必须为 ${error.multipleOfValue} 的倍数`;
1386
+ let newWord;
1387
+ const forceWord = forceArray[forceArrayLower.indexOf(word.toLowerCase())];
1388
+ if (!forceWord) {
1389
+ if (noInitialCase) {
1390
+ if (word.slice(1).search(/\../) !== -1) {
1391
+ newWord = word.toLowerCase();
1392
+ }
1393
+ else {
1394
+ newWord = word[0].toUpperCase() + word.slice(1).toLowerCase();
1395
+ }
1396
+ }
1397
+ else {
1398
+ newWord = word[0].toUpperCase() + word.slice(1);
1399
+ }
1400
+ }
1401
+ else if (forceWord === forceWord.toLowerCase() && (idx === 0 || idx + word.length === input.length ||
1402
+ prevLastChar === ':' || input[idx - 1].search(/[^\s-]/) !== -1 ||
1403
+ (input[idx - 1] !== '-' && input[idx + word.length] === '-'))) {
1404
+ newWord = forceWord[0].toUpperCase() + forceWord.slice(1);
1405
+ }
1406
+ else {
1407
+ newWord = forceWord;
1408
+ }
1409
+ prevLastChar = word.slice(-1);
1410
+ return newWord;
1390
1411
  }
1391
- },
1392
- minProperties: '项目数必须大于或者等于 {{minimumProperties}} (当前项目数: {{currentProperties}})',
1393
- maxProperties: '项目数必须小于或者等于 {{maximumProperties}} (当前项目数: {{currentProperties}})',
1394
- minItems: '项目数必须大于或者等于 {{minimumItems}} (当前项目数: {{currentItems}})',
1395
- maxItems: '项目数必须小于或者等于 {{maximumItems}} (当前项目数: {{currentItems}})',
1396
- uniqueItems: '所有项目必须是唯一的',
1397
- // Note: No default error messages for 'type', 'const', 'enum', or 'dependencies'
1398
- };
1412
+ });
1413
+ }
1399
1414
 
1400
1415
  class JsonPointer {
1401
1416
  /**
@@ -6357,6 +6372,12 @@ function buildTitleMap(titleMap, enumList, fieldRequired = true, flatList = true
6357
6372
  // causes a library to be imported before another library it depends on.
6358
6373
 
6359
6374
  class JsonSchemaFormService {
6375
+ setDraggableState(value) {
6376
+ this.draggableStateSubject.next(value); // Update the draggable value
6377
+ }
6378
+ setSortableOptions(value) {
6379
+ this.sortableOptionsSubject.next(value); // Update the sortable options value
6380
+ }
6360
6381
  constructor() {
6361
6382
  this.JsonFormCompatibility = false;
6362
6383
  this.ReactJsonSchemaFormCompatibility = false;
@@ -6441,6 +6462,18 @@ class JsonSchemaFormService {
6441
6462
  validationMessages: {} // set by setLanguage()
6442
6463
  }
6443
6464
  };
6465
+ //this has been added to enable or disable the dragabble state of a component
6466
+ //using the OrderableDirective, mainly when an <input type="range">
6467
+ //elements are present, as the draggable attribute makes it difficult to
6468
+ //slide the range sliders and end up dragging
6469
+ //NB this will be set globally for all OrderableDirective instances
6470
+ //and will only be enabled when/if the caller sets the value back to true
6471
+ this.draggableStateSubject = new BehaviorSubject(true); // Default value true
6472
+ this.draggableState$ = this.draggableStateSubject.asObservable();
6473
+ //this is introduced to look at replacing the orderable directive with
6474
+ //nxt-sortablejs and sortablejs
6475
+ this.sortableOptionsSubject = new BehaviorSubject({ disabled: false }); // Default value true
6476
+ this.sortableOptions$ = this.sortableOptionsSubject.asObservable();
6444
6477
  this.setLanguage(this.language);
6445
6478
  this.ajv.addMetaSchema(jsonDraft6);
6446
6479
  this.ajv.addMetaSchema(jsonDraft7);
@@ -6992,7 +7025,7 @@ class JsonSchemaFormService {
6992
7025
  JsonPointer.insert(this.layout, this.getLayoutPointer(ctx), newLayoutNode);
6993
7026
  return true;
6994
7027
  }
6995
- moveArrayItem(ctx, oldIndex, newIndex) {
7028
+ moveArrayItem(ctx, oldIndex, newIndex, moveLayout = true) {
6996
7029
  if (!ctx || !ctx.layoutNode ||
6997
7030
  !isDefined(ctx.layoutNode().dataPointer) ||
6998
7031
  !hasValue(ctx.dataIndex()) ||
@@ -7009,8 +7042,10 @@ class JsonSchemaFormService {
7009
7042
  formArray.insert(newIndex, arrayItem);
7010
7043
  formArray.updateValueAndValidity();
7011
7044
  // Move layout item
7012
- const layoutArray = this.getLayoutArray(ctx);
7013
- layoutArray.splice(newIndex, 0, layoutArray.splice(oldIndex, 1)[0]);
7045
+ if (moveLayout) {
7046
+ const layoutArray = this.getLayoutArray(ctx);
7047
+ layoutArray.splice(newIndex, 0, layoutArray.splice(oldIndex, 1)[0]);
7048
+ }
7014
7049
  return true;
7015
7050
  }
7016
7051
  removeItem(ctx) {
@@ -7040,6 +7075,104 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7040
7075
  type: Injectable
7041
7076
  }], ctorParameters: () => [] });
7042
7077
 
7078
+ class SelectWidgetComponent {
7079
+ constructor() {
7080
+ this.jsf = inject(JsonSchemaFormService);
7081
+ this.newComponent = null;
7082
+ this.layoutNode = input(undefined);
7083
+ this.layoutIndex = input(undefined);
7084
+ this.dataIndex = input(undefined);
7085
+ this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
7086
+ }
7087
+ ngOnInit() {
7088
+ this.updateComponent();
7089
+ }
7090
+ ngOnChanges() {
7091
+ this.updateComponent();
7092
+ }
7093
+ updateComponent() {
7094
+ const widgetContainer = this.widgetContainer();
7095
+ if (widgetContainer && !this.newComponent && (this.layoutNode() || {}).widget) {
7096
+ this.newComponent = widgetContainer.createComponent((this.layoutNode().widget));
7097
+ }
7098
+ if (this.newComponent) {
7099
+ for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
7100
+ this.newComponent.instance[input] = this[input];
7101
+ }
7102
+ }
7103
+ }
7104
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7105
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "17.3.12", type: SelectWidgetComponent, selector: "select-widget-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "widgetContainer", first: true, predicate: ["widgetContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `<div #widgetContainer></div>`, isInline: true }); }
7106
+ }
7107
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectWidgetComponent, decorators: [{
7108
+ type: Component,
7109
+ args: [{
7110
+ // tslint:disable-next-line:component-selector
7111
+ selector: 'select-widget-widget',
7112
+ template: `<div #widgetContainer></div>`,
7113
+ }]
7114
+ }] });
7115
+
7116
+ class NoFrameworkComponent {
7117
+ constructor() {
7118
+ this.layoutNode = input(undefined);
7119
+ this.layoutIndex = input(undefined);
7120
+ this.dataIndex = input(undefined);
7121
+ }
7122
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7123
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: NoFrameworkComponent, selector: "no-framework", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<select-widget-widget [dataIndex]=\"dataIndex()\" [layoutIndex]=\"layoutIndex()\" [layoutNode]=\"layoutNode()\">\r\n</select-widget-widget>", dependencies: [{ kind: "component", type: SelectWidgetComponent, selector: "select-widget-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
7124
+ }
7125
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkComponent, decorators: [{
7126
+ type: Component,
7127
+ args: [{ selector: 'no-framework', template: "<select-widget-widget [dataIndex]=\"dataIndex()\" [layoutIndex]=\"layoutIndex()\" [layoutNode]=\"layoutNode()\">\r\n</select-widget-widget>" }]
7128
+ }] });
7129
+
7130
+ // No framework - plain HTML controls (styles from form layout only)
7131
+ class NoFramework extends Framework {
7132
+ constructor() {
7133
+ super(...arguments);
7134
+ this.name = 'no-framework';
7135
+ this.text = 'None (plain HTML)';
7136
+ this.framework = NoFrameworkComponent;
7137
+ }
7138
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFramework, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
7139
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFramework }); }
7140
+ }
7141
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFramework, decorators: [{
7142
+ type: Injectable
7143
+ }] });
7144
+
7145
+ class ElementAttributeDirective {
7146
+ constructor(renderer, elementRef) {
7147
+ this.renderer = renderer;
7148
+ this.elementRef = elementRef;
7149
+ }
7150
+ ngOnChanges(changes) {
7151
+ if (changes.attributes) {
7152
+ for (let attributeName in this.attributes) {
7153
+ const attributeValue = this.attributes[attributeName];
7154
+ if (attributeValue) {
7155
+ this.renderer.setAttribute(this.elementRef.nativeElement, attributeName, attributeValue);
7156
+ }
7157
+ else {
7158
+ this.renderer.removeAttribute(this.elementRef.nativeElement, attributeName);
7159
+ }
7160
+ }
7161
+ }
7162
+ }
7163
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ElementAttributeDirective, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
7164
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ElementAttributeDirective, selector: "[attributes]", inputs: { attributes: "attributes" }, usesOnChanges: true, ngImport: i0 }); }
7165
+ }
7166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ElementAttributeDirective, decorators: [{
7167
+ type: Directive,
7168
+ args: [{
7169
+ selector: '[attributes]',
7170
+ standalone: false
7171
+ }]
7172
+ }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }], propDecorators: { attributes: [{
7173
+ type: Input
7174
+ }] } });
7175
+
7043
7176
  class AddReferenceComponent {
7044
7177
  constructor() {
7045
7178
  this.jsf = inject(JsonSchemaFormService);
@@ -7425,6 +7558,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7425
7558
  }]
7426
7559
  }] });
7427
7560
 
7561
+ class HiddenComponent {
7562
+ constructor() {
7563
+ this.jsf = inject(JsonSchemaFormService);
7564
+ this.controlDisabled = false;
7565
+ this.boundControl = false;
7566
+ this.layoutNode = input(undefined);
7567
+ this.layoutIndex = input(undefined);
7568
+ this.dataIndex = input(undefined);
7569
+ }
7570
+ ngOnInit() {
7571
+ this.jsf.initializeControl(this);
7572
+ }
7573
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7574
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: HiddenComponent, selector: "hidden-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7575
+ <input *ngIf="boundControl"
7576
+ [formControl]="formControl"
7577
+ [id]="'control' + layoutNode()?._id"
7578
+ [name]="controlName"
7579
+ type="hidden">
7580
+ <input *ngIf="!boundControl"
7581
+ [disabled]="controlDisabled"
7582
+ [name]="controlName"
7583
+ [id]="'control' + layoutNode()?._id"
7584
+ type="hidden"
7585
+ [value]="controlValue">`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
7586
+ }
7587
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HiddenComponent, decorators: [{
7588
+ type: Component,
7589
+ args: [{
7590
+ // tslint:disable-next-line:component-selector
7591
+ selector: 'hidden-widget',
7592
+ template: `
7593
+ <input *ngIf="boundControl"
7594
+ [formControl]="formControl"
7595
+ [id]="'control' + layoutNode()?._id"
7596
+ [name]="controlName"
7597
+ type="hidden">
7598
+ <input *ngIf="!boundControl"
7599
+ [disabled]="controlDisabled"
7600
+ [name]="controlName"
7601
+ [id]="'control' + layoutNode()?._id"
7602
+ type="hidden"
7603
+ [value]="controlValue">`,
7604
+ }]
7605
+ }] });
7606
+
7428
7607
  class InputComponent {
7429
7608
  constructor() {
7430
7609
  this.jsf = inject(JsonSchemaFormService);
@@ -7435,6 +7614,10 @@ class InputComponent {
7435
7614
  this.layoutIndex = input(undefined);
7436
7615
  this.dataIndex = input(undefined);
7437
7616
  }
7617
+ //needed as templates don't accept something like [attributes]="options?.['x-inputAttributes']"
7618
+ get inputAttributes() {
7619
+ return this.options?.['x-inputAttributes'];
7620
+ }
7438
7621
  ngOnInit() {
7439
7622
  this.options = this.layoutNode().options || {};
7440
7623
  this.jsf.initializeControl(this);
@@ -7444,7 +7627,7 @@ class InputComponent {
7444
7627
  }
7445
7628
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7446
7629
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: InputComponent, selector: "input-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7447
- <div [class]="options?.htmlClass || ''">
7630
+ <div [class]="options?.htmlClass || ''" class="sortable-filter" >
7448
7631
  <label *ngIf="options?.title"
7449
7632
  [attr.for]="'control' + layoutNode()?._id"
7450
7633
  [class]="options?.labelHtmlClass || ''"
@@ -7463,7 +7646,9 @@ class InputComponent {
7463
7646
  [id]="'control' + layoutNode()?._id"
7464
7647
  [name]="controlName"
7465
7648
  [readonly]="options?.readonly ? 'readonly' : null"
7466
- [type]="layoutNode()?.type">
7649
+ [type]="layoutNode()?.type"
7650
+ [attributes]="inputAttributes"
7651
+ >
7467
7652
  <input *ngIf="!boundControl"
7468
7653
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7469
7654
  [attr.list]="'control' + layoutNode()?._id + 'Autocomplete'"
@@ -7479,12 +7664,14 @@ class InputComponent {
7479
7664
  [readonly]="options?.readonly ? 'readonly' : null"
7480
7665
  [type]="layoutNode()?.type"
7481
7666
  [value]="controlValue"
7482
- (input)="updateValue($event)">
7667
+ (input)="updateValue($event)"
7668
+ [attributes]="inputAttributes"
7669
+ >
7483
7670
  <datalist *ngIf="options?.typeahead?.source"
7484
7671
  [id]="'control' + layoutNode()?._id + 'Autocomplete'">
7485
7672
  <option *ngFor="let word of options?.typeahead?.source" [value]="word">
7486
7673
  </datalist>
7487
- </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
7674
+ </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: ElementAttributeDirective, selector: "[attributes]", inputs: ["attributes"] }] }); }
7488
7675
  }
7489
7676
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputComponent, decorators: [{
7490
7677
  type: Component,
@@ -7492,7 +7679,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7492
7679
  // tslint:disable-next-line:component-selector
7493
7680
  selector: 'input-widget',
7494
7681
  template: `
7495
- <div [class]="options?.htmlClass || ''">
7682
+ <div [class]="options?.htmlClass || ''" class="sortable-filter" >
7496
7683
  <label *ngIf="options?.title"
7497
7684
  [attr.for]="'control' + layoutNode()?._id"
7498
7685
  [class]="options?.labelHtmlClass || ''"
@@ -7511,7 +7698,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7511
7698
  [id]="'control' + layoutNode()?._id"
7512
7699
  [name]="controlName"
7513
7700
  [readonly]="options?.readonly ? 'readonly' : null"
7514
- [type]="layoutNode()?.type">
7701
+ [type]="layoutNode()?.type"
7702
+ [attributes]="inputAttributes"
7703
+ >
7515
7704
  <input *ngIf="!boundControl"
7516
7705
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7517
7706
  [attr.list]="'control' + layoutNode()?._id + 'Autocomplete'"
@@ -7527,7 +7716,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7527
7716
  [readonly]="options?.readonly ? 'readonly' : null"
7528
7717
  [type]="layoutNode()?.type"
7529
7718
  [value]="controlValue"
7530
- (input)="updateValue($event)">
7719
+ (input)="updateValue($event)"
7720
+ [attributes]="inputAttributes"
7721
+ >
7531
7722
  <datalist *ngIf="options?.typeahead?.source"
7532
7723
  [id]="'control' + layoutNode()?._id + 'Autocomplete'">
7533
7724
  <option *ngFor="let word of options?.typeahead?.source" [value]="word">
@@ -7585,6 +7776,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7585
7776
  }]
7586
7777
  }] });
7587
7778
 
7779
+ //TODO look at reusing InputComponent
7588
7780
  class NumberComponent {
7589
7781
  constructor() {
7590
7782
  this.jsf = inject(JsonSchemaFormService);
@@ -7598,6 +7790,10 @@ class NumberComponent {
7598
7790
  this.layoutIndex = input(undefined);
7599
7791
  this.dataIndex = input(undefined);
7600
7792
  }
7793
+ //needed as templates don't accept something like [attributes]="options?.['x-inputAttributes']"
7794
+ get inputAttributes() {
7795
+ return this.options?.['x-inputAttributes'];
7796
+ }
7601
7797
  ngOnInit() {
7602
7798
  this.options = this.layoutNode().options || {};
7603
7799
  this.jsf.initializeControl(this);
@@ -7609,14 +7805,14 @@ class NumberComponent {
7609
7805
  this.jsf.updateValue(this, event.target.value);
7610
7806
  }
7611
7807
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NumberComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7612
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: NumberComponent, selector: "number-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7613
- <div [class]="options?.htmlClass || ''">
7808
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: NumberComponent, selector: "number-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "inputControl", first: true, predicate: ["inputControl"], descendants: true }, { propertyName: "div", first: true, predicate: ["divElt"], descendants: true }], ngImport: i0, template: `
7809
+ <div #divElt [class]="options?.htmlClass || ''" class="sortable-filter" >
7614
7810
  <label *ngIf="options?.title"
7615
7811
  [attr.for]="'control' + layoutNode()?._id"
7616
7812
  [class]="options?.labelHtmlClass || ''"
7617
7813
  [style.display]="options?.notitle ? 'none' : ''"
7618
7814
  [innerHTML]="options?.title"></label>
7619
- <input *ngIf="boundControl"
7815
+ <input #inputControl *ngIf="boundControl"
7620
7816
  [formControl]="formControl"
7621
7817
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7622
7818
  [attr.max]="options?.maximum"
@@ -7630,8 +7826,11 @@ class NumberComponent {
7630
7826
  [name]="controlName"
7631
7827
  [readonly]="options?.readonly ? 'readonly' : null"
7632
7828
  [title]="lastValidNumber"
7633
- [type]="layoutNode()?.type === 'range' ? 'range' : 'number'">
7634
- <input *ngIf="!boundControl"
7829
+ [type]="layoutNode()?.type === 'range' ? 'range' : 'number'"
7830
+ [attributes]="inputAttributes"
7831
+
7832
+ >
7833
+ <input #inputControl *ngIf="!boundControl"
7635
7834
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7636
7835
  [attr.max]="options?.maximum"
7637
7836
  [attr.min]="options?.minimum"
@@ -7647,9 +7846,11 @@ class NumberComponent {
7647
7846
  [title]="lastValidNumber"
7648
7847
  [type]="layoutNode()?.type === 'range' ? 'range' : 'number'"
7649
7848
  [value]="controlValue"
7650
- (input)="updateValue($event)">
7849
+ (input)="updateValue($event)"
7850
+ [attributes]="inputAttributes"
7851
+ >
7651
7852
  <span *ngIf="layoutNode()?.type === 'range'" [innerHTML]="controlValue"></span>
7652
- </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
7853
+ </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: ElementAttributeDirective, selector: "[attributes]", inputs: ["attributes"] }] }); }
7653
7854
  }
7654
7855
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NumberComponent, decorators: [{
7655
7856
  type: Component,
@@ -7657,13 +7858,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7657
7858
  // tslint:disable-next-line:component-selector
7658
7859
  selector: 'number-widget',
7659
7860
  template: `
7660
- <div [class]="options?.htmlClass || ''">
7861
+ <div #divElt [class]="options?.htmlClass || ''" class="sortable-filter" >
7661
7862
  <label *ngIf="options?.title"
7662
7863
  [attr.for]="'control' + layoutNode()?._id"
7663
7864
  [class]="options?.labelHtmlClass || ''"
7664
7865
  [style.display]="options?.notitle ? 'none' : ''"
7665
7866
  [innerHTML]="options?.title"></label>
7666
- <input *ngIf="boundControl"
7867
+ <input #inputControl *ngIf="boundControl"
7667
7868
  [formControl]="formControl"
7668
7869
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7669
7870
  [attr.max]="options?.maximum"
@@ -7677,8 +7878,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7677
7878
  [name]="controlName"
7678
7879
  [readonly]="options?.readonly ? 'readonly' : null"
7679
7880
  [title]="lastValidNumber"
7680
- [type]="layoutNode()?.type === 'range' ? 'range' : 'number'">
7681
- <input *ngIf="!boundControl"
7881
+ [type]="layoutNode()?.type === 'range' ? 'range' : 'number'"
7882
+ [attributes]="inputAttributes"
7883
+
7884
+ >
7885
+ <input #inputControl *ngIf="!boundControl"
7682
7886
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7683
7887
  [attr.max]="options?.maximum"
7684
7888
  [attr.min]="options?.minimum"
@@ -7694,11 +7898,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7694
7898
  [title]="lastValidNumber"
7695
7899
  [type]="layoutNode()?.type === 'range' ? 'range' : 'number'"
7696
7900
  [value]="controlValue"
7697
- (input)="updateValue($event)">
7901
+ (input)="updateValue($event)"
7902
+ [attributes]="inputAttributes"
7903
+ >
7698
7904
  <span *ngIf="layoutNode()?.type === 'range'" [innerHTML]="controlValue"></span>
7699
7905
  </div>`,
7700
7906
  }]
7701
- }] });
7907
+ }], propDecorators: { inputControl: [{
7908
+ type: ViewChild,
7909
+ args: ['inputControl', {}]
7910
+ }], div: [{
7911
+ type: ViewChild,
7912
+ args: ['divElt', {}]
7913
+ }] } });
7702
7914
 
7703
7915
  // TODO: Add this control
7704
7916
  class OneOfComponent {
@@ -7877,7 +8089,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7877
8089
 
7878
8090
  class SelectFrameworkComponent {
7879
8091
  constructor() {
7880
- this.componentFactory = inject(ComponentFactoryResolver);
7881
8092
  this.jsf = inject(JsonSchemaFormService);
7882
8093
  this.newComponent = null;
7883
8094
  this.layoutNode = input(undefined);
@@ -7894,7 +8105,7 @@ class SelectFrameworkComponent {
7894
8105
  updateComponent() {
7895
8106
  const widgetContainer = this.widgetContainer();
7896
8107
  if (widgetContainer && !this.newComponent && this.jsf.framework) {
7897
- this.newComponent = widgetContainer.createComponent(this.componentFactory.resolveComponentFactory(this.jsf.framework));
8108
+ this.newComponent = widgetContainer.createComponent((this.jsf.framework));
7898
8109
  //TODO fix all deprecated calls and test
7899
8110
  //this.widgetContainer.createComponent<any>(this.jsf.framework)
7900
8111
  }
@@ -8020,6 +8231,15 @@ class OrderableDirective {
8020
8231
  return false;
8021
8232
  });
8022
8233
  });
8234
+ // Subscribe to the draggable state
8235
+ this.draggableStateSubscription = this.jsf.draggableState$.subscribe((value) => {
8236
+ this.element.draggable = value;
8237
+ });
8238
+ }
8239
+ }
8240
+ ngOnDestroy() {
8241
+ if (this.draggableStateSubscription) {
8242
+ this.draggableStateSubscription.unsubscribe();
8023
8243
  }
8024
8244
  }
8025
8245
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OrderableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
@@ -8041,10 +8261,42 @@ class RootComponent {
8041
8261
  this.layout = input(undefined);
8042
8262
  this.isOrderable = input(undefined);
8043
8263
  this.isFlexItem = input(false);
8264
+ this.sortableConfig = {
8265
+ filter: ".sortable-filter",
8266
+ preventOnFilter: false,
8267
+ delay: 1000,
8268
+ delayOnTouchOnly: true,
8269
+ onEnd: (/**Event*/ evt) => {
8270
+ evt.newIndex; // most likely why this event is used is to get the dragging element's current index
8271
+ // same properties as onEnd
8272
+ //console.log(`sortablejs event:${evt}`);
8273
+ let srcInd = evt.oldIndex;
8274
+ let trgInd = evt.newIndex;
8275
+ let layoutItem = this.layout()[trgInd];
8276
+ let dataInd = layoutItem?.arrayItem ? (this.dataIndex() || []).concat(trgInd) : (this.dataIndex() || []);
8277
+ let layoutInd = (this.layoutIndex() || []).concat(trgInd);
8278
+ let itemCtx = {
8279
+ dataIndex: () => { return dataInd; },
8280
+ layoutIndex: () => { return layoutInd; },
8281
+ layoutNode: () => { return layoutItem; },
8282
+ };
8283
+ //must set moveLayout to false as nxtSortable already moves it
8284
+ this.jsf.moveArrayItem(itemCtx, evt.oldIndex, evt.newIndex, false);
8285
+ }
8286
+ };
8287
+ }
8288
+ sortableInit(sortable) {
8289
+ this.sortableObj = sortable;
8044
8290
  }
8045
8291
  isDraggable(node) {
8046
- return node.arrayItem && node.type !== '$ref' &&
8292
+ let result = node.arrayItem && node.type !== '$ref' &&
8047
8293
  node.arrayItemType === 'list' && this.isOrderable() !== false;
8294
+ if (this.sortableObj) {
8295
+ //this.sortableObj.option("disabled",true);
8296
+ //this.sortableObj.option("sort",false);
8297
+ //this.sortableObj.option("disabled",!result);
8298
+ }
8299
+ return result;
8048
8300
  }
8049
8301
  // Set attributes for flexbox child
8050
8302
  // (container attributes are set in section.component)
@@ -8056,48 +8308,86 @@ class RootComponent {
8056
8308
  showWidget(layoutNode) {
8057
8309
  return this.jsf.evaluateCondition(layoutNode, this.dataIndex());
8058
8310
  }
8311
+ ngOnInit() {
8312
+ // Subscribe to the draggable state
8313
+ this.sortableOptionsSubscription = this.jsf.sortableOptions$.subscribe((optsValue) => {
8314
+ if (this.sortableObj) {
8315
+ Object.keys(optsValue).forEach(opt => {
8316
+ let optVal = optsValue[opt];
8317
+ this.sortableObj.option(opt, optVal);
8318
+ });
8319
+ //this.sortableObj.option("disabled",true);
8320
+ //this.sortableObj.option("sort",false);
8321
+ }
8322
+ });
8323
+ }
8324
+ ngOnDestroy() {
8325
+ if (this.sortableOptionsSubscription) {
8326
+ this.sortableOptionsSubscription.unsubscribe();
8327
+ }
8328
+ }
8059
8329
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RootComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8060
8330
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: RootComponent, selector: "root-widget", inputs: { dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, isOrderable: { classPropertyName: "isOrderable", publicName: "isOrderable", isSignal: true, isRequired: false, transformFunction: null }, isFlexItem: { classPropertyName: "isFlexItem", publicName: "isFlexItem", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8061
- <div *ngFor="let layoutItem of layout(); let i = index"
8062
- [class.form-flex-item]="isFlexItem()"
8063
- [style.align-self]="(layoutItem.options || {})['align-self']"
8064
- [style.flex-basis]="getFlexAttribute(layoutItem, 'flex-basis')"
8065
- [style.flex-grow]="getFlexAttribute(layoutItem, 'flex-grow')"
8066
- [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8067
- [style.order]="(layoutItem.options || {}).order">
8068
- <div
8069
- [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
8070
- [layoutIndex]="(layoutIndex() || []).concat(i)"
8071
- [layoutNode]="layoutItem"
8072
- [orderable]="isDraggable(layoutItem)">
8073
- <select-framework-widget *ngIf="showWidget(layoutItem)"
8331
+ <div #sortableContainter [nxtSortablejs]="layout()" [config]="sortableConfig" (init)="sortableInit($event)">
8332
+ <div *ngFor="let layoutItem of layout(); let i = index"
8333
+ [class.form-flex-item]="isFlexItem()"
8334
+ [style.align-self]="(layoutItem.options || {})['align-self']"
8335
+ [style.flex-basis]="getFlexAttribute(layoutItem, 'flex-basis')"
8336
+ [style.flex-grow]="getFlexAttribute(layoutItem, 'flex-grow')"
8337
+ [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8338
+ [style.order]="(layoutItem.options || {}).order"
8339
+ [class.sortable-filter]="!isDraggable(layoutItem)"
8340
+ >
8341
+ <!--NB orderable directive is not used but has been left in for now and set to false
8342
+ otherwise the compiler won't recognize dataIndex and other dependent attributes
8343
+ -->
8344
+ <div
8074
8345
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
8075
8346
  [layoutIndex]="(layoutIndex() || []).concat(i)"
8076
- [layoutNode]="layoutItem"></select-framework-widget>
8347
+ [layoutNode]="layoutItem"
8348
+ [orderable]="false"
8349
+ [class.sortable-filter]="!isDraggable(layoutItem)"
8350
+ >
8351
+ <select-framework-widget *ngIf="showWidget(layoutItem)"
8352
+ [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
8353
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8354
+ [layoutNode]="layoutItem"></select-framework-widget>
8355
+ </div>
8077
8356
  </div>
8078
- </div>`, isInline: true, styles: ["[draggable=true]{transition:all .15s cubic-bezier(.4,0,.2,1)}[draggable=true]:hover{cursor:move;box-shadow:2px 2px 4px #0003;position:relative;z-index:10;margin:-1px 1px 1px -1px}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }, { kind: "directive", type: OrderableDirective, selector: "[orderable]", inputs: ["orderable", "layoutNode", "layoutIndex", "dataIndex"] }] }); }
8357
+ </div>
8358
+ `, isInline: true, styles: ["[draggable=true]{transition:all .15s cubic-bezier(.4,0,.2,1)}[draggable=true]:hover{cursor:move;box-shadow:2px 2px 4px #0003;position:relative;z-index:10;margin:-1px 1px 1px -1px}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.SortablejsDirective, selector: "[nxtSortablejs]", inputs: ["nxtSortablejs", "sortablejsContainer", "config", "cloneFunction"], outputs: ["init"] }, { kind: "component", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }, { kind: "directive", type: OrderableDirective, selector: "[orderable]", inputs: ["orderable", "layoutNode", "layoutIndex", "dataIndex"] }] }); }
8079
8359
  }
8080
8360
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RootComponent, decorators: [{
8081
8361
  type: Component,
8082
8362
  args: [{ selector: 'root-widget', template: `
8083
- <div *ngFor="let layoutItem of layout(); let i = index"
8084
- [class.form-flex-item]="isFlexItem()"
8085
- [style.align-self]="(layoutItem.options || {})['align-self']"
8086
- [style.flex-basis]="getFlexAttribute(layoutItem, 'flex-basis')"
8087
- [style.flex-grow]="getFlexAttribute(layoutItem, 'flex-grow')"
8088
- [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8089
- [style.order]="(layoutItem.options || {}).order">
8090
- <div
8091
- [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
8092
- [layoutIndex]="(layoutIndex() || []).concat(i)"
8093
- [layoutNode]="layoutItem"
8094
- [orderable]="isDraggable(layoutItem)">
8095
- <select-framework-widget *ngIf="showWidget(layoutItem)"
8363
+ <div #sortableContainter [nxtSortablejs]="layout()" [config]="sortableConfig" (init)="sortableInit($event)">
8364
+ <div *ngFor="let layoutItem of layout(); let i = index"
8365
+ [class.form-flex-item]="isFlexItem()"
8366
+ [style.align-self]="(layoutItem.options || {})['align-self']"
8367
+ [style.flex-basis]="getFlexAttribute(layoutItem, 'flex-basis')"
8368
+ [style.flex-grow]="getFlexAttribute(layoutItem, 'flex-grow')"
8369
+ [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8370
+ [style.order]="(layoutItem.options || {}).order"
8371
+ [class.sortable-filter]="!isDraggable(layoutItem)"
8372
+ >
8373
+ <!--NB orderable directive is not used but has been left in for now and set to false
8374
+ otherwise the compiler won't recognize dataIndex and other dependent attributes
8375
+ -->
8376
+ <div
8096
8377
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
8097
8378
  [layoutIndex]="(layoutIndex() || []).concat(i)"
8098
- [layoutNode]="layoutItem"></select-framework-widget>
8379
+ [layoutNode]="layoutItem"
8380
+ [orderable]="false"
8381
+ [class.sortable-filter]="!isDraggable(layoutItem)"
8382
+ >
8383
+ <select-framework-widget *ngIf="showWidget(layoutItem)"
8384
+ [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
8385
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8386
+ [layoutNode]="layoutItem"></select-framework-widget>
8387
+ </div>
8099
8388
  </div>
8100
- </div>`, styles: ["[draggable=true]{transition:all .15s cubic-bezier(.4,0,.2,1)}[draggable=true]:hover{cursor:move;box-shadow:2px 2px 4px #0003;position:relative;z-index:10;margin:-1px 1px 1px -1px}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"] }]
8389
+ </div>
8390
+ `, standalone: false, styles: ["[draggable=true]{transition:all .15s cubic-bezier(.4,0,.2,1)}[draggable=true]:hover{cursor:move;box-shadow:2px 2px 4px #0003;position:relative;z-index:10;margin:-1px 1px 1px -1px}[draggable=true].drag-target-top{box-shadow:0 -2px #000;position:relative;z-index:20}[draggable=true].drag-target-bottom{box-shadow:0 2px #000;position:relative;z-index:20}\n"] }]
8101
8391
  }] });
8102
8392
 
8103
8393
  class SectionComponent {
@@ -8172,7 +8462,7 @@ class SectionComponent {
8172
8462
  [class]="options?.labelHtmlClass || ''"
8173
8463
  [innerHTML]="sectionTitle"
8174
8464
  (click)="toggleExpanded()"></label>
8175
- <root-widget *ngIf="expanded"
8465
+ <root-widget
8176
8466
  [dataIndex]="dataIndex()"
8177
8467
  [layout]="layoutNode().items"
8178
8468
  [layoutIndex]="layoutIndex()"
@@ -8182,7 +8472,7 @@ class SectionComponent {
8182
8472
  [class.form-flex-row]="getFlexAttribute('flex-direction') === 'row'"
8183
8473
  [style.align-content]="getFlexAttribute('align-content')"
8184
8474
  [style.align-items]="getFlexAttribute('align-items')"
8185
- [style.display]="getFlexAttribute('display')"
8475
+ [style.display]="!expanded?'none':getFlexAttribute('display')"
8186
8476
  [style.flex-direction]="getFlexAttribute('flex-direction')"
8187
8477
  [style.flex-wrap]="getFlexAttribute('flex-wrap')"
8188
8478
  [style.justify-content]="getFlexAttribute('justify-content')"></root-widget>
@@ -8203,7 +8493,7 @@ class SectionComponent {
8203
8493
  [class]="options?.labelHelpBlockClass || ''"
8204
8494
  [innerHTML]="options?.description"></p>
8205
8495
  </div>
8206
- <root-widget *ngIf="expanded"
8496
+ <root-widget
8207
8497
  [dataIndex]="dataIndex()"
8208
8498
  [layout]="layoutNode().items"
8209
8499
  [layoutIndex]="layoutIndex()"
@@ -8213,7 +8503,7 @@ class SectionComponent {
8213
8503
  [class.form-flex-row]="getFlexAttribute('flex-direction') === 'row'"
8214
8504
  [style.align-content]="getFlexAttribute('align-content')"
8215
8505
  [style.align-items]="getFlexAttribute('align-items')"
8216
- [style.display]="getFlexAttribute('display')"
8506
+ [style.display]="!expanded?'none':getFlexAttribute('display')"
8217
8507
  [style.flex-direction]="getFlexAttribute('flex-direction')"
8218
8508
  [style.flex-wrap]="getFlexAttribute('flex-wrap')"
8219
8509
  [style.justify-content]="getFlexAttribute('justify-content')"></root-widget>
@@ -8237,7 +8527,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8237
8527
  [class]="options?.labelHtmlClass || ''"
8238
8528
  [innerHTML]="sectionTitle"
8239
8529
  (click)="toggleExpanded()"></label>
8240
- <root-widget *ngIf="expanded"
8530
+ <root-widget
8241
8531
  [dataIndex]="dataIndex()"
8242
8532
  [layout]="layoutNode().items"
8243
8533
  [layoutIndex]="layoutIndex()"
@@ -8247,7 +8537,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8247
8537
  [class.form-flex-row]="getFlexAttribute('flex-direction') === 'row'"
8248
8538
  [style.align-content]="getFlexAttribute('align-content')"
8249
8539
  [style.align-items]="getFlexAttribute('align-items')"
8250
- [style.display]="getFlexAttribute('display')"
8540
+ [style.display]="!expanded?'none':getFlexAttribute('display')"
8251
8541
  [style.flex-direction]="getFlexAttribute('flex-direction')"
8252
8542
  [style.flex-wrap]="getFlexAttribute('flex-wrap')"
8253
8543
  [style.justify-content]="getFlexAttribute('justify-content')"></root-widget>
@@ -8268,7 +8558,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8268
8558
  [class]="options?.labelHelpBlockClass || ''"
8269
8559
  [innerHTML]="options?.description"></p>
8270
8560
  </div>
8271
- <root-widget *ngIf="expanded"
8561
+ <root-widget
8272
8562
  [dataIndex]="dataIndex()"
8273
8563
  [layout]="layoutNode().items"
8274
8564
  [layoutIndex]="layoutIndex()"
@@ -8278,7 +8568,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8278
8568
  [class.form-flex-row]="getFlexAttribute('flex-direction') === 'row'"
8279
8569
  [style.align-content]="getFlexAttribute('align-content')"
8280
8570
  [style.align-items]="getFlexAttribute('align-items')"
8281
- [style.display]="getFlexAttribute('display')"
8571
+ [style.display]="!expanded?'none':getFlexAttribute('display')"
8282
8572
  [style.flex-direction]="getFlexAttribute('flex-direction')"
8283
8573
  [style.flex-wrap]="getFlexAttribute('flex-wrap')"
8284
8574
  [style.justify-content]="getFlexAttribute('justify-content')"></root-widget>
@@ -8399,75 +8689,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8399
8689
  <option *ngFor="let subItem of selectItem.items"
8400
8690
  [value]="subItem?.value">
8401
8691
  <span [innerHTML]="subItem?.name"></span>
8402
- </option>
8403
- </optgroup>
8404
- </ng-template>
8405
- </select>
8406
- <select *ngIf="!boundControl"
8407
- [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8408
- [attr.readonly]="options?.readonly ? 'readonly' : null"
8409
- [attr.required]="options?.required"
8410
- [class]="options?.fieldHtmlClass || ''"
8411
- [disabled]="controlDisabled"
8412
- [id]="'control' + layoutNode()?._id"
8413
- [name]="controlName"
8414
- (change)="updateValue($event)">
8415
- <ng-template ngFor let-selectItem [ngForOf]="selectList">
8416
- <option *ngIf="!isArray(selectItem?.items)"
8417
- [selected]="selectItem?.value === controlValue"
8418
- [value]="selectItem?.value">
8419
- <span [innerHTML]="selectItem?.name"></span>
8420
- </option>
8421
- <optgroup *ngIf="isArray(selectItem?.items)"
8422
- [label]="selectItem?.group">
8423
- <option *ngFor="let subItem of selectItem.items"
8424
- [attr.selected]="subItem?.value === controlValue"
8425
- [value]="subItem?.value">
8426
- <span [innerHTML]="subItem?.name"></span>
8427
- </option>
8428
- </optgroup>
8429
- </ng-template>
8430
- </select>
8431
- </div>`,
8432
- }]
8433
- }] });
8434
-
8435
- class SelectWidgetComponent {
8436
- constructor() {
8437
- this.componentFactory = inject(ComponentFactoryResolver);
8438
- this.jsf = inject(JsonSchemaFormService);
8439
- this.newComponent = null;
8440
- this.layoutNode = input(undefined);
8441
- this.layoutIndex = input(undefined);
8442
- this.dataIndex = input(undefined);
8443
- this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
8444
- }
8445
- ngOnInit() {
8446
- this.updateComponent();
8447
- }
8448
- ngOnChanges() {
8449
- this.updateComponent();
8450
- }
8451
- updateComponent() {
8452
- const widgetContainer = this.widgetContainer();
8453
- if (widgetContainer && !this.newComponent && (this.layoutNode() || {}).widget) {
8454
- this.newComponent = widgetContainer.createComponent(this.componentFactory.resolveComponentFactory(this.layoutNode().widget));
8455
- }
8456
- if (this.newComponent) {
8457
- for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
8458
- this.newComponent.instance[input] = this[input];
8459
- }
8460
- }
8461
- }
8462
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8463
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "17.3.12", type: SelectWidgetComponent, selector: "select-widget-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "widgetContainer", first: true, predicate: ["widgetContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `<div #widgetContainer></div>`, isInline: true }); }
8464
- }
8465
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectWidgetComponent, decorators: [{
8466
- type: Component,
8467
- args: [{
8468
- // tslint:disable-next-line:component-selector
8469
- selector: 'select-widget-widget',
8470
- template: `<div #widgetContainer></div>`,
8692
+ </option>
8693
+ </optgroup>
8694
+ </ng-template>
8695
+ </select>
8696
+ <select *ngIf="!boundControl"
8697
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8698
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
8699
+ [attr.required]="options?.required"
8700
+ [class]="options?.fieldHtmlClass || ''"
8701
+ [disabled]="controlDisabled"
8702
+ [id]="'control' + layoutNode()?._id"
8703
+ [name]="controlName"
8704
+ (change)="updateValue($event)">
8705
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
8706
+ <option *ngIf="!isArray(selectItem?.items)"
8707
+ [selected]="selectItem?.value === controlValue"
8708
+ [value]="selectItem?.value">
8709
+ <span [innerHTML]="selectItem?.name"></span>
8710
+ </option>
8711
+ <optgroup *ngIf="isArray(selectItem?.items)"
8712
+ [label]="selectItem?.group">
8713
+ <option *ngFor="let subItem of selectItem.items"
8714
+ [attr.selected]="subItem?.value === controlValue"
8715
+ [value]="subItem?.value">
8716
+ <span [innerHTML]="subItem?.name"></span>
8717
+ </option>
8718
+ </optgroup>
8719
+ </ng-template>
8720
+ </select>
8721
+ </div>`,
8471
8722
  }]
8472
8723
  }] });
8473
8724
 
@@ -8546,6 +8797,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8546
8797
  }]
8547
8798
  }] });
8548
8799
 
8800
+ class TabComponent {
8801
+ constructor() {
8802
+ this.jsf = inject(JsonSchemaFormService);
8803
+ this.layoutNode = input(undefined);
8804
+ this.layoutIndex = input(undefined);
8805
+ this.dataIndex = input(undefined);
8806
+ }
8807
+ ngOnInit() {
8808
+ this.options = this.layoutNode().options || {};
8809
+ }
8810
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8811
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: TabComponent, selector: "tab-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8812
+ <div [class]="options?.htmlClass || ''">
8813
+ <root-widget
8814
+ [dataIndex]="dataIndex()"
8815
+ [layoutIndex]="layoutIndex()"
8816
+ [layout]="layoutNode().items"></root-widget>
8817
+ </div>`, isInline: true, dependencies: [{ kind: "component", type: RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }] }); }
8818
+ }
8819
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabComponent, decorators: [{
8820
+ type: Component,
8821
+ args: [{
8822
+ // tslint:disable-next-line:component-selector
8823
+ selector: 'tab-widget',
8824
+ template: `
8825
+ <div [class]="options?.htmlClass || ''">
8826
+ <root-widget
8827
+ [dataIndex]="dataIndex()"
8828
+ [layoutIndex]="layoutIndex()"
8829
+ [layout]="layoutNode().items"></root-widget>
8830
+ </div>`,
8831
+ }]
8832
+ }] });
8833
+
8549
8834
  class TabsComponent {
8550
8835
  constructor() {
8551
8836
  this.jsf = inject(JsonSchemaFormService);
@@ -8649,7 +8934,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8649
8934
 
8650
8935
  class TemplateComponent {
8651
8936
  constructor() {
8652
- this.componentFactory = inject(ComponentFactoryResolver);
8653
8937
  this.jsf = inject(JsonSchemaFormService);
8654
8938
  this.newComponent = null;
8655
8939
  this.layoutNode = input(undefined);
@@ -8667,7 +8951,7 @@ class TemplateComponent {
8667
8951
  const layoutNode = this.layoutNode();
8668
8952
  const widgetContainer = this.widgetContainer();
8669
8953
  if (widgetContainer && !this.newComponent && layoutNode.options.template) {
8670
- this.newComponent = widgetContainer.createComponent(this.componentFactory.resolveComponentFactory(layoutNode.options.template));
8954
+ this.newComponent = widgetContainer.createComponent((layoutNode.options.template));
8671
8955
  }
8672
8956
  if (this.newComponent) {
8673
8957
  for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
@@ -8986,17 +9270,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8986
9270
  }]
8987
9271
  }], ctorParameters: () => [] });
8988
9272
 
8989
- class Framework {
8990
- constructor() {
8991
- this.widgets = {};
8992
- this.stylesheets = [];
8993
- this.scripts = [];
8994
- }
8995
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: Framework, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8996
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: Framework }); }
9273
+ const BASIC_WIDGETS = [
9274
+ AddReferenceComponent, OneOfComponent, ButtonComponent, CheckboxComponent,
9275
+ CheckboxesComponent, FileComponent, HiddenComponent, InputComponent,
9276
+ MessageComponent, NoneComponent, NumberComponent, RadiosComponent,
9277
+ RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent,
9278
+ SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent,
9279
+ TemplateComponent, TextareaComponent
9280
+ ];
9281
+
9282
+ class WidgetLibraryModule {
9283
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
9284
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, declarations: [AddReferenceComponent, OneOfComponent, ButtonComponent, CheckboxComponent, CheckboxesComponent, FileComponent, HiddenComponent, InputComponent, MessageComponent, NoneComponent, NumberComponent, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, OrderableDirective, ElementAttributeDirective], imports: [CommonModule, FormsModule, ReactiveFormsModule, i2$1.SortablejsModule], exports: [AddReferenceComponent, OneOfComponent, ButtonComponent, CheckboxComponent, CheckboxesComponent, FileComponent, HiddenComponent, InputComponent, MessageComponent, NoneComponent, NumberComponent, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, OrderableDirective, ElementAttributeDirective] }); }
9285
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, imports: [CommonModule, FormsModule, ReactiveFormsModule,
9286
+ SortablejsModule.forRoot({
9287
+ //disabled:false,
9288
+ //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9289
+ filter: ".sortable-filter",
9290
+ preventOnFilter: false //needed for input range elements slider do still work
9291
+ })] }); }
8997
9292
  }
8998
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: Framework, decorators: [{
8999
- type: Injectable
9293
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, decorators: [{
9294
+ type: NgModule,
9295
+ args: [{
9296
+ imports: [CommonModule, FormsModule, ReactiveFormsModule,
9297
+ SortablejsModule.forRoot({
9298
+ //disabled:false,
9299
+ //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9300
+ filter: ".sortable-filter",
9301
+ preventOnFilter: false //needed for input range elements slider do still work
9302
+ })],
9303
+ declarations: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective],
9304
+ exports: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective]
9305
+ }]
9306
+ }] });
9307
+
9308
+ // No framework - plain HTML controls (styles from form layout only)
9309
+ class NoFrameworkModule {
9310
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
9311
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, declarations: [NoFrameworkComponent], imports: [CommonModule, WidgetLibraryModule], exports: [NoFrameworkComponent] }); }
9312
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, providers: [
9313
+ { provide: Framework, useClass: NoFramework, multi: true }
9314
+ ], imports: [CommonModule, WidgetLibraryModule] }); }
9315
+ }
9316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, decorators: [{
9317
+ type: NgModule,
9318
+ args: [{
9319
+ imports: [CommonModule, WidgetLibraryModule],
9320
+ declarations: [NoFrameworkComponent],
9321
+ exports: [NoFrameworkComponent],
9322
+ providers: [
9323
+ { provide: Framework, useClass: NoFramework, multi: true }
9324
+ ]
9325
+ }]
9000
9326
  }] });
9001
9327
 
9002
9328
  // Possible future frameworks:
@@ -9363,7 +9689,7 @@ class JsonSchemaFormComponent {
9363
9689
  }
9364
9690
  // Get names of changed inputs
9365
9691
  let changedInput = Object.keys(this.previousInputs)
9366
- .filter(input => this.previousInputs[input] !== this[input]);
9692
+ .filter(input => this.previousInputs[input] !== this.getInputValue(input));
9367
9693
  let resetFirst = true;
9368
9694
  if (changedInput.length === 1 && changedInput[0] === 'form' &&
9369
9695
  this.formValuesInput.startsWith('form.')) {
@@ -9376,12 +9702,13 @@ class JsonSchemaFormComponent {
9376
9702
  // If only input values have changed, update the form values
9377
9703
  if (changedInput.length === 1 && changedInput[0] === this.formValuesInput) {
9378
9704
  if (this.formValuesInput.indexOf('.') === -1) {
9379
- changedData = this[this.formValuesInput];
9705
+ changedData = this.getInputValue(this.formValuesInput);
9706
+ //this[this.formValuesInput];
9380
9707
  this.setFormValues(changedData, resetFirst);
9381
9708
  }
9382
9709
  else {
9383
9710
  const [input, key] = this.formValuesInput.split('.');
9384
- changedData = this[input][key];
9711
+ changedData = this.getInputValue(input)[key];
9385
9712
  this.setFormValues(changedData, resetFirst);
9386
9713
  }
9387
9714
  // If anything else has changed, re-render the entire form
@@ -9402,7 +9729,7 @@ class JsonSchemaFormComponent {
9402
9729
  }
9403
9730
  // Update previous inputs
9404
9731
  Object.keys(this.previousInputs)
9405
- .filter(input => this.previousInputs[input] !== this[input])
9732
+ .filter(input => this.previousInputs[input] !== this.getInputValue(input))
9406
9733
  .forEach(input => this.previousInputs[input] = this.getInputValue(input));
9407
9734
  }
9408
9735
  }
@@ -9905,158 +10232,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
9905
10232
  type: Input
9906
10233
  }] } });
9907
10234
 
9908
- class NoFrameworkComponent {
9909
- constructor() {
9910
- this.layoutNode = input(undefined);
9911
- this.layoutIndex = input(undefined);
9912
- this.dataIndex = input(undefined);
9913
- }
9914
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9915
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: NoFrameworkComponent, selector: "no-framework", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<select-widget-widget [dataIndex]=\"dataIndex()\" [layoutIndex]=\"layoutIndex()\" [layoutNode]=\"layoutNode()\">\r\n</select-widget-widget>", dependencies: [{ kind: "component", type: SelectWidgetComponent, selector: "select-widget-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
9916
- }
9917
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkComponent, decorators: [{
9918
- type: Component,
9919
- args: [{ selector: 'no-framework', template: "<select-widget-widget [dataIndex]=\"dataIndex()\" [layoutIndex]=\"layoutIndex()\" [layoutNode]=\"layoutNode()\">\r\n</select-widget-widget>" }]
9920
- }] });
9921
-
9922
- // No framework - plain HTML controls (styles from form layout only)
9923
- class NoFramework extends Framework {
9924
- constructor() {
9925
- super(...arguments);
9926
- this.name = 'no-framework';
9927
- this.text = 'None (plain HTML)';
9928
- this.framework = NoFrameworkComponent;
9929
- }
9930
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFramework, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
9931
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFramework }); }
9932
- }
9933
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFramework, decorators: [{
9934
- type: Injectable
9935
- }] });
9936
-
9937
- class HiddenComponent {
9938
- constructor() {
9939
- this.jsf = inject(JsonSchemaFormService);
9940
- this.controlDisabled = false;
9941
- this.boundControl = false;
9942
- this.layoutNode = input(undefined);
9943
- this.layoutIndex = input(undefined);
9944
- this.dataIndex = input(undefined);
9945
- }
9946
- ngOnInit() {
9947
- this.jsf.initializeControl(this);
9948
- }
9949
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9950
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: HiddenComponent, selector: "hidden-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
9951
- <input *ngIf="boundControl"
9952
- [formControl]="formControl"
9953
- [id]="'control' + layoutNode()?._id"
9954
- [name]="controlName"
9955
- type="hidden">
9956
- <input *ngIf="!boundControl"
9957
- [disabled]="controlDisabled"
9958
- [name]="controlName"
9959
- [id]="'control' + layoutNode()?._id"
9960
- type="hidden"
9961
- [value]="controlValue">`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
9962
- }
9963
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HiddenComponent, decorators: [{
9964
- type: Component,
9965
- args: [{
9966
- // tslint:disable-next-line:component-selector
9967
- selector: 'hidden-widget',
9968
- template: `
9969
- <input *ngIf="boundControl"
9970
- [formControl]="formControl"
9971
- [id]="'control' + layoutNode()?._id"
9972
- [name]="controlName"
9973
- type="hidden">
9974
- <input *ngIf="!boundControl"
9975
- [disabled]="controlDisabled"
9976
- [name]="controlName"
9977
- [id]="'control' + layoutNode()?._id"
9978
- type="hidden"
9979
- [value]="controlValue">`,
9980
- }]
9981
- }] });
9982
-
9983
- class TabComponent {
9984
- constructor() {
9985
- this.jsf = inject(JsonSchemaFormService);
9986
- this.layoutNode = input(undefined);
9987
- this.layoutIndex = input(undefined);
9988
- this.dataIndex = input(undefined);
9989
- }
9990
- ngOnInit() {
9991
- this.options = this.layoutNode().options || {};
9992
- }
9993
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9994
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: TabComponent, selector: "tab-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
9995
- <div [class]="options?.htmlClass || ''">
9996
- <root-widget
9997
- [dataIndex]="dataIndex()"
9998
- [layoutIndex]="layoutIndex()"
9999
- [layout]="layoutNode().items"></root-widget>
10000
- </div>`, isInline: true, dependencies: [{ kind: "component", type: RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }] }); }
10001
- }
10002
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabComponent, decorators: [{
10003
- type: Component,
10004
- args: [{
10005
- // tslint:disable-next-line:component-selector
10006
- selector: 'tab-widget',
10007
- template: `
10008
- <div [class]="options?.htmlClass || ''">
10009
- <root-widget
10010
- [dataIndex]="dataIndex()"
10011
- [layoutIndex]="layoutIndex()"
10012
- [layout]="layoutNode().items"></root-widget>
10013
- </div>`,
10014
- }]
10015
- }] });
10016
-
10017
- const BASIC_WIDGETS = [
10018
- AddReferenceComponent, OneOfComponent, ButtonComponent, CheckboxComponent,
10019
- CheckboxesComponent, FileComponent, HiddenComponent, InputComponent,
10020
- MessageComponent, NoneComponent, NumberComponent, RadiosComponent,
10021
- RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent,
10022
- SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent,
10023
- TemplateComponent, TextareaComponent
10024
- ];
10025
-
10026
- class WidgetLibraryModule {
10027
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
10028
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, declarations: [AddReferenceComponent, OneOfComponent, ButtonComponent, CheckboxComponent, CheckboxesComponent, FileComponent, HiddenComponent, InputComponent, MessageComponent, NoneComponent, NumberComponent, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, OrderableDirective], imports: [CommonModule, FormsModule, ReactiveFormsModule], exports: [AddReferenceComponent, OneOfComponent, ButtonComponent, CheckboxComponent, CheckboxesComponent, FileComponent, HiddenComponent, InputComponent, MessageComponent, NoneComponent, NumberComponent, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, OrderableDirective] }); }
10029
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, imports: [CommonModule, FormsModule, ReactiveFormsModule] }); }
10030
- }
10031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, decorators: [{
10032
- type: NgModule,
10033
- args: [{
10034
- imports: [CommonModule, FormsModule, ReactiveFormsModule],
10035
- declarations: [...BASIC_WIDGETS, OrderableDirective],
10036
- exports: [...BASIC_WIDGETS, OrderableDirective]
10037
- }]
10038
- }] });
10039
-
10040
- // No framework - plain HTML controls (styles from form layout only)
10041
- class NoFrameworkModule {
10042
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
10043
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, declarations: [NoFrameworkComponent], imports: [CommonModule, WidgetLibraryModule], exports: [NoFrameworkComponent] }); }
10044
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, providers: [
10045
- { provide: Framework, useClass: NoFramework, multi: true }
10046
- ], imports: [CommonModule, WidgetLibraryModule] }); }
10047
- }
10048
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NoFrameworkModule, decorators: [{
10049
- type: NgModule,
10050
- args: [{
10051
- imports: [CommonModule, WidgetLibraryModule],
10052
- declarations: [NoFrameworkComponent],
10053
- exports: [NoFrameworkComponent],
10054
- providers: [
10055
- { provide: Framework, useClass: NoFramework, multi: true }
10056
- ]
10057
- }]
10058
- }] });
10059
-
10060
10235
  class JsonSchemaFormModule {
10061
10236
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: JsonSchemaFormModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
10062
10237
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: JsonSchemaFormModule, declarations: [JsonSchemaFormComponent], imports: [CommonModule, FormsModule, ReactiveFormsModule,
@@ -10084,5 +10259,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
10084
10259
  * Generated bundle index. Do not edit.
10085
10260
  */
10086
10261
 
10087
- export { AddReferenceComponent, BASIC_WIDGETS, ButtonComponent, CheckboxComponent, CheckboxesComponent, FileComponent, Framework, FrameworkLibraryService, HiddenComponent, InputComponent, JsonPointer, JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonValidators, MessageComponent, NoneComponent, NumberComponent, OneOfComponent, OrderableDirective, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, WidgetLibraryModule, WidgetLibraryService, _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, _toPromise, addClasses, buildFormGroup, buildFormGroupTemplate, buildLayout, buildLayoutFromSchema, buildSchemaFromData, buildSchemaFromLayout, buildTitleMap, checkInlineType, combineAllOf, commonItems, convertSchemaToDraft6, copy, deValidationMessages, enValidationMessages, esValidationMessages, fixRequiredArrayProperties, fixTitle, forEach, forEachCopy, formatFormData, frValidationMessages, getControl, getControlValidators, getFromSchema, getInputType, getLayoutNode, getSubSchema, getTitleMapFromOneOf, getType, hasOwn, hasValue, inArray, isArray, isBoolean, isDate, isDefined, isEmpty, isFunction, isInputRequired, isInteger, isMap, isNumber, isObject, isObservable, isPrimitive, isPromise, isSet, isString, isType, itValidationMessages, mapLayout, mergeFilteredObject, mergeSchemas, ptValidationMessages, removeRecursiveReferences, resolveSchemaReferences, setRequiredFields, toJavaScriptType, toObservable, toSchemaType, toTitleCase, uniqueItems, updateInputOptions, xor, zhValidationMessages };
10262
+ export { AddReferenceComponent, BASIC_WIDGETS, ButtonComponent, CheckboxComponent, CheckboxesComponent, ElementAttributeDirective, FileComponent, Framework, FrameworkLibraryService, HiddenComponent, InputComponent, JsonPointer, JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonValidators, MessageComponent, NoneComponent, NumberComponent, OneOfComponent, OrderableDirective, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, WidgetLibraryModule, WidgetLibraryService, _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, _toPromise, addClasses, buildFormGroup, buildFormGroupTemplate, buildLayout, buildLayoutFromSchema, buildSchemaFromData, buildSchemaFromLayout, buildTitleMap, checkInlineType, combineAllOf, commonItems, convertSchemaToDraft6, copy, deValidationMessages, enValidationMessages, esValidationMessages, fixRequiredArrayProperties, fixTitle, forEach, forEachCopy, formatFormData, frValidationMessages, getControl, getControlValidators, getFromSchema, getInputType, getLayoutNode, getSubSchema, getTitleMapFromOneOf, getType, hasOwn, hasValue, inArray, isArray, isBoolean, isDate, isDefined, isEmpty, isFunction, isInputRequired, isInteger, isMap, isNumber, isObject, isObservable, isPrimitive, isPromise, isSet, isString, isType, itValidationMessages, mapLayout, mergeFilteredObject, mergeSchemas, ptValidationMessages, removeRecursiveReferences, resolveSchemaReferences, setRequiredFields, toJavaScriptType, toObservable, toSchemaType, toTitleCase, uniqueItems, updateInputOptions, xor, zhValidationMessages };
10088
10263
  //# sourceMappingURL=ng-formworks-core.mjs.map