@optionfactory/ful 0.19.0 → 0.21.0

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.
package/dist/ful.iife.js CHANGED
@@ -1,105 +1,6 @@
1
1
  var ful = (function (exports) {
2
2
  'use strict';
3
3
 
4
- /* global CSS */
5
-
6
- function extract(extractors, el) {
7
- const maybeExtractor = extractors[el.dataset['bindExtractor']] || extractors[el.dataset['bindProvide']];
8
- if (maybeExtractor) {
9
- return maybeExtractor(el);
10
- }
11
- if (el.getAttribute('type') === 'radio') {
12
- if (!el.checked) {
13
- return undefined;
14
- }
15
- return el.dataset['bindType'] === 'boolean' ? el.value === 'true' : el.value;
16
- }
17
- if (el.getAttribute('type') === 'checkbox') {
18
- return el.checked;
19
- }
20
- if (el.dataset['bindType'] === 'boolean') {
21
- return !el.value ? null : el.value === 'true';
22
- }
23
- return el.value || null;
24
- }
25
-
26
- function mutate(mutators, el, raw, key, values) {
27
- const maybeMutator = mutators[el.dataset['bindMutator']] || mutators[el.dataset['bindProvide']];
28
- if (maybeMutator) {
29
- maybeMutator(el, raw, key, values);
30
- return;
31
- }
32
- if (el.getAttribute('type') === 'radio') {
33
- el.checked = el.getAttribute('value') === raw;
34
- return;
35
- }
36
- if (el.getAttribute('type') === 'checkbox') {
37
- el.checked = raw;
38
- return;
39
- }
40
- el.value = raw;
41
- }
42
-
43
-
44
- function providePath(result, path, value) {
45
- const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
46
- let current = result;
47
- let previous = null;
48
- for (let i = 0; ; ++i) {
49
- const ckey = keys[i];
50
- const pkey = keys[i - 1];
51
- if (Number.isInteger(ckey) && !Array.isArray(current)) {
52
- if (previous !== null) {
53
- previous[pkey] = current = [];
54
- } else {
55
- result = current = [];
56
- }
57
- }
58
- if (i === keys.length - 1) {
59
- //when value is undefined we only want to define the property if it's not defined
60
- current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
61
- return result;
62
- }
63
- if (current[ckey] === undefined) {
64
- current[ckey] = {};
65
- }
66
- previous = current;
67
- current = current[ckey];
68
- }
69
- }
70
-
71
- class Bindings {
72
-
73
- constructor( {extractors, mutators, ignoredChildrenSelector, valueHoldersSelector}) {
74
- this.extractors = extractors || {};
75
- this.mutators = mutators || {};
76
- this.valueHoldersSelector = valueHoldersSelector || 'input[name], select[name], textarea[name]';
77
- this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none';
78
- }
79
- setValues(el, values) {
80
- for (let k in values) {
81
- if (!values.hasOwnProperty(k)) {
82
- continue;
83
- }
84
- Array.from(el.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
85
- mutate(this.mutators, el, values[k], k, values);
86
- });
87
- }
88
- }
89
- getValues(el) {
90
- return Array.from(el.querySelectorAll(this.valueHoldersSelector))
91
- .filter((el) => {
92
- if (el.dataset['bindInclude'] === 'never') {
93
- return false;
94
- }
95
- return el.dataset['bindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
96
- })
97
- .reduce((result, el) => {
98
- return providePath(result, el.getAttribute('name'), extract(this.extractors, el));
99
- }, {});
100
- }
101
- }
102
-
103
4
  class Base64 {
104
5
  static encode(arrayBuffer, dialect) {
105
6
  const d = dialect || Base64.URL_SAFE;
@@ -172,69 +73,44 @@ var ful = (function (exports) {
172
73
  }
173
74
  }
174
75
 
175
- /* global Infinity, CSS */
176
-
177
-
178
- class Form {
179
- constructor(el, bindings, {globalErrorsEl, fieldContainerSelector, errorClass, hideClass}) {
180
- this.el = el;
181
- this.bindings = bindings;
182
- this.globalErrorsEl = globalErrorsEl;
183
- this.fieldContainerSelector = fieldContainerSelector !== undefined ? fieldContainerSelector : Form.DEFAULT_FIELD_CONTAINER_SELECTOR;
184
- this.errorClass = errorClass || Form.DEFAULT_ERROR_CLASS;
185
- this.hideClass = hideClass || Form.DEFAULT_HIDE_CLASS;
186
- }
187
- setValues(values) {
188
- return this.bindings.setValues(this.el, values);
189
- }
190
- getValues() {
191
- return this.bindings.getValues(this.el);
76
+ class Observable {
77
+ constructor() {
78
+ this.listeners = {};
192
79
  }
193
- setErrors(errors, scrollFirstErrorIntoView, context) {
194
-
195
- this.clearErrors();
196
- errors
197
- .map(this.mapError ? this.mapError : (e) => e)
198
- .filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
199
- .forEach((e) => {
200
- const name = e.context.replace("[", ".").replace("].", ".");
201
- Array.from(this.el.querySelectorAll(`[name='${CSS.escape(name)}']`))
202
- .map(el => this.fieldContainerSelector ? el.closest(this.fieldContainerSelector) : el)
203
- .filter(el => el !== null)
204
- .forEach(label => {
205
- label.classList.add(this.errorClass);
206
- label.dataset['error'] = e.reason;
207
- });
208
- });
209
- if (this.globalErrorsEl) {
210
- const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
211
- this.globalErrorsEl.innerHTML = globalErrors.map(e => e.reason).join("\n");
212
- if (globalErrors.length !== 0) {
213
- this.globalErrorsEl.classList.remove(this.hideClass);
214
- }
215
- }
216
- if (!scrollFirstErrorIntoView) {
217
- return;
218
- }
219
- const yOffsets = Array.from(this.el.querySelectorAll('.${CSS.escape(this.errorClass)}'))
220
- .map((label) => label.getBoundingClientRect().y + window.scrollY);
221
- const firstErrorScrollY = Math.min(...yOffsets);
222
- if (firstErrorScrollY !== Infinity) {
223
- window.scroll(window.scrollX, firstErrorScrollY > 100 ? firstErrorScrollY - 100 : 0);
80
+ fireSync(event, data, initialAcc) {
81
+ const listeners = this.listeners[event] || [];
82
+ let acc = initialAcc;
83
+ for (const l of listeners) {
84
+ acc = l(data, this, acc);
224
85
  }
86
+ return acc;
225
87
  }
226
- clearErrors() {
227
- this.el.querySelectorAll(`.${CSS.escape(this.errorClass)}`).forEach(l => l.classList.remove(this.errorClass));
228
- if (this.globalErrorsEl) {
229
- this.globalErrorsEl.innerHTML = '';
230
- this.globalErrorsEl.classList.add(this.hideClass);
88
+ async fire(event, data, initialAcc) {
89
+ const listeners = this.listeners[event] || [];
90
+ let acc = initialAcc;
91
+ for (const l of listeners) {
92
+ acc = await l(data, this, acc);
231
93
  }
94
+ return acc;
95
+ }
96
+ on(event, listener) {
97
+ this.listeners[event] = this.listeners[event] || [];
98
+ this.listeners[event].push(listener);
99
+ }
100
+ un(event, listener) {
101
+ const listeners = this.listeners[event] || [];
102
+ const idx = listeners.indexOf(listener);
103
+ return idx === -1 ? [] : listeners.splice(idx, 1);
104
+ }
105
+ static mixin(self) {
106
+ self.listeners = {};
107
+ self.fireSync = Observable.prototype.fireSync;
108
+ self.fire = Observable.prototype.fire;
109
+ self.on = Observable.prototype.on;
110
+ self.un = Observable.prototype.un;
232
111
  }
233
- }
234
112
 
235
- Form.DEFAULT_FIELD_CONTAINER_SELECTOR = 'label';
236
- Form.DEFAULT_ERROR_CLASS = 'has-error';
237
- Form.DEFAULT_HIDE_CLASS = 'd-none';
113
+ }
238
114
 
239
115
  class ContextInterceptor {
240
116
  constructor() {
@@ -351,9 +227,10 @@ var ful = (function (exports) {
351
227
  this.interceptors = interceptors || [];
352
228
  }
353
229
  async fetch(resource, options) {
354
- const interceptors = [...this.interceptors, ...options.interceptors || [], new HttpCall()];
230
+ const opts = options || {};
231
+ const interceptors = [...this.interceptors, ...opts.interceptors || [], new HttpCall()];
355
232
  const chain = new HttpInterceptorChain(interceptors, 0);
356
- return await chain.proceed({resource, options});
233
+ return await chain.proceed({resource, opts});
357
234
  }
358
235
  async json(resource, options) {
359
236
  try {
@@ -376,30 +253,467 @@ var ful = (function (exports) {
376
253
  }]);
377
254
  }
378
255
  }
379
- async form(resource, options, uiOptions) {
380
- const ui = uiOptions || {};
381
- ui.buttons?.forEach(el => {
382
- el.setAttribute("disabled", "disabled");
383
- if (ui.loader) {
384
- el.dataset['oldContent'] = el.innerHTML;
385
- el.innerHTML = ui.loader;
256
+ }
257
+
258
+ function jsonRequest(method, body, headers){
259
+ return {
260
+ headers: {
261
+ "Content-Type": "application/json",
262
+ ...headers
263
+ },
264
+ method: method,
265
+ body: JSON.stringify(body)
266
+ }
267
+ }
268
+
269
+ /* global Infinity, CSS */
270
+
271
+ class CustomElements {
272
+ static id = 0;
273
+ static uid(prefix) {
274
+ return `${prefix}-${++CustomElements.id}`;
275
+ }
276
+ static forwardAttributes(from, to, except) {
277
+ from.getAttributeNames().filter(a => except.indexOf(a) === -1)
278
+ .filter(a => a[0] === '@')
279
+ .forEach(a => {
280
+ if (a === '@class') {
281
+ to.classList.add(...from.getAttribute("@class").split(" ").filter(a => a.length));
282
+ return;
283
+ }
284
+ to.setAttribute(a.substring(1), from.getAttribute(a));
285
+ });
286
+ }
287
+ static extractSlots(el) {
288
+ const slotted = Object.fromEntries([...el.querySelectorAll("[slot]")].map(el => {
289
+ el.parentElement.removeChild(el);
290
+ const slot = el.getAttribute("slot");
291
+ el.removeAttribute("slot");
292
+ return [slot, el];
293
+ }));
294
+ slotted.default = new DocumentFragment();
295
+ slotted.default.append(...el.childNodes);
296
+ return slotted;
297
+ }
298
+ static labelAndInputGroup(id, name, isFloating, slotted) {
299
+ if (isFloating) {
300
+ /**
301
+ * <div class="input-group has-validation">
302
+ * <span data-tpl-if="slotted.before" class="input-group-text">{{{{ slotted.before }}}}</span>
303
+ * <div class="form-floating">
304
+ * {{{{ slotted.input }}}}
305
+ * <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
306
+ * </div>
307
+ * <span data-tpl-if="slotted.after" class="input-group-text">{{{{ slotted.after }}}}</span>
308
+ * <ful-field-error data-tpl-field="name"></ful-field-error>
309
+ * </div>
310
+ */
311
+ const label = document.createElement("label");
312
+ label.setAttribute("for", id);
313
+ label.classList.add('form-label');
314
+ label.append(slotted.default);
315
+
316
+ const ff = document.createElement('div');
317
+ ff.classList.add("form-floating");
318
+ ff.append(slotted.input, label);
319
+
320
+ const ffe = document.createElement('ful-field-error');
321
+ ffe.setAttribute("field", name);
322
+
323
+ const ig = document.createElement("div");
324
+ ig.classList.add('input-group', 'has-validtion');
325
+
326
+ if (slotted.before) {
327
+ ig.append(slotted.before);
328
+ } else if (slotted.ibefore) {
329
+ const igt = document.createElement('div');
330
+ igt.classList.add('input-group-text');
331
+ igt.append(slotted.ibefore);
332
+ ig.append(igt);
333
+ }
334
+ ig.append(ff);
335
+ if (slotted.after) {
336
+ ig.append(slotted.after);
337
+ } else if (slotted.iafter) {
338
+ const igt = document.createElement('div');
339
+ igt.classList.add('input-group-text');
340
+ igt.append(slotted.iafter);
341
+ ig.append(igt);
342
+ }
343
+ ig.append(ffe);
344
+ return ig;
345
+ }
346
+ /**
347
+ <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
348
+ <div class="input-group has-validation">
349
+ <span data-tpl-if="slotted.before" class="input-group-text">{{{{ slotted.before }}}}</span>
350
+ {{{{ slotted.input }}}}
351
+ <span data-tpl-if="slotted.after" class="input-group-text">{{{{ slotted.after }}}}</span>
352
+ <ful-field-error data-tpl-field="name"></ful-field-error>
353
+ </div>
354
+ */
355
+
356
+ const label = document.createElement("label");
357
+ label.setAttribute("for", name);
358
+ label.classList.add('form-label');
359
+ label.append(slotted.default);
360
+
361
+ const ffe = document.createElement('ful-field-error');
362
+ ffe.setAttribute("field", name);
363
+
364
+ const ig = document.createElement("div");
365
+ ig.classList.add('input-group', 'has-validation');
366
+
367
+ if (slotted.before) {
368
+ ig.append(slotted.before);
369
+ } else if (slotted.ibefore) {
370
+ const igt = document.createElement('div');
371
+ igt.classList.add('input-group-text');
372
+ igt.append(slotted.ibefore);
373
+ ig.append(igt);
374
+ }
375
+ ig.append(slotted.input);
376
+ if (slotted.after) {
377
+ ig.append(slotted.after);
378
+ } else if (slotted.iafter) {
379
+ const igt = document.createElement('div');
380
+ igt.classList.add('input-group-text');
381
+ igt.append(slotted.iafter);
382
+ ig.append(igt);
383
+ }
384
+ ig.append(ffe);
385
+
386
+ const fragment = new DocumentFragment();
387
+ fragment.append(label, ig);
388
+ return fragment;
389
+ }
390
+
391
+ }
392
+
393
+
394
+ class FieldError extends HTMLElement {
395
+ constructor() {
396
+ super();
397
+ }
398
+ connectedCallback() {
399
+ this.classList.add('invalid-feedback');
400
+ }
401
+ static configure() {
402
+ customElements.define('ful-field-error', FieldError);
403
+ }
404
+ }
405
+
406
+ class Errors extends HTMLElement {
407
+ constructor() {
408
+ super();
409
+ }
410
+ connectedCallback() {
411
+ this.classList.add('alert', 'alert-danger', 'd-none');
412
+ }
413
+ static configure() {
414
+ customElements.define('ful-errors', Errors);
415
+ }
416
+
417
+ }
418
+
419
+ class Spinner extends HTMLElement {
420
+ constructor() {
421
+ super();
422
+ }
423
+ connectedCallback() {
424
+ this.classList.add('spinner-border', 'spinner-border-sm', 'd-none');
425
+ this.setAttribute("aria-hidden", "true");
426
+ }
427
+ show() {
428
+ this.classList.remove("d-none");
429
+ }
430
+ hide() {
431
+ this.classList.add("d-none");
432
+ }
433
+ static configure() {
434
+ customElements.define('ful-spinner', Spinner);
435
+ }
436
+ }
437
+
438
+
439
+
440
+ class Input extends HTMLElement {
441
+ constructor() {
442
+ super();
443
+ const id = CustomElements.uid('ful-input');
444
+ const name = this.getAttribute('@name');
445
+ const floating = this.hasAttribute('@floating');
446
+ const slotted = CustomElements.extractSlots(this);
447
+ slotted.input = slotted.input || (() => {
448
+ const el = document.createElement("input");
449
+ el.classList.add("form-control");
450
+ return el;
451
+ })();
452
+ CustomElements.forwardAttributes(this, slotted.input, ['@floating']);
453
+ const attrIfMissing = (el, k, v) => !el.hasAttribute(k) && el.setAttribute(k, v);
454
+ attrIfMissing(slotted.input, "name", id);
455
+ attrIfMissing(slotted.input, "id", id);
456
+ attrIfMissing(slotted.input, "type", "text");
457
+ attrIfMissing(slotted.input, "placeholder", " ");
458
+ this.innerHTML = '';
459
+ this.append(CustomElements.labelAndInputGroup(id, name || id, floating, slotted));
460
+ }
461
+ static configure() {
462
+ customElements.define('ful-input', Input);
463
+ }
464
+ }
465
+
466
+
467
+
468
+ /**
469
+ * <script src="tom-select.complete.js"></script>
470
+ * <link href="tom-select.bootstrap5.css" rel="stylesheet" />
471
+ */
472
+ class Select extends HTMLElement {
473
+ constructor(tsConfig) {
474
+ super();
475
+ Observable.mixin(this);
476
+ const id = CustomElements.uid('ful-select');
477
+ const name = this.getAttribute('@name');
478
+ const floating = this.hasAttribute('@floating');
479
+ const remote = this.hasAttribute('@remote');
480
+ const slotted = CustomElements.extractSlots(this);
481
+ slotted.input = slotted.input || (() => {
482
+ return document.createElement("select");
483
+ })();
484
+ CustomElements.forwardAttributes(this, slotted.input, ['@floating', '@remote']);
485
+ const attrIfMissing = (el, k, v) => !el.hasAttribute(k) && el.setAttribute(k, v);
486
+ attrIfMissing(slotted.input, "name", id);
487
+ attrIfMissing(slotted.input, "id", id);
488
+ attrIfMissing(slotted.input, "placeholder", " ");
489
+ this.innerHTML = '';
490
+ this.append(CustomElements.labelAndInputGroup(id, name || id, floating, slotted));
491
+ this.loaded = !remote;
492
+ this.ts = new TomSelect(slotted.input, Object.assign(remote ? {
493
+ preload: 'focus',
494
+ load: async (query, callback) => {
495
+ if (this.loaded) {
496
+ callback();
497
+ return;
498
+ }
499
+ const data = await this.fire('load', query, []);
500
+ this.loaded = true;
501
+ callback(data);
502
+ }
503
+ } : {}, tsConfig));
504
+ slotted.input.setValue = this.setValue.bind(this);
505
+ slotted.input.getValue = this.getValue.bind(this);
506
+ }
507
+ async setValue(v){
508
+ if(!this.loaded){
509
+ await this.ts.load();
510
+ }
511
+ this.ts.setValue(v);
512
+ }
513
+ getValue(){
514
+ const v = this.ts.getValue();
515
+ return v === '' ? null : v;
516
+ }
517
+ static custom(tagName, configuration) {
518
+ customElements.define(tagName, class extends Select {
519
+ constructor() {
520
+ super(configuration);
386
521
  }
387
522
  });
388
- try {
389
- const r = await this.json(resource, options);
390
- ui.form?.clearErrors();
391
- return r;
392
- } catch (e) {
393
- ui.form?.setErrors(e.problems);
394
- throw e;
395
- } finally {
396
- ui.buttons?.forEach(el => {
397
- el.removeAttribute("disabled");
398
- el.innerHTML = el.dataset['oldContent'];
399
- delete el.dataset['oldContent'];
523
+ }
524
+ static configure() {
525
+ return Select.custom('ful-select');
526
+ }
527
+
528
+ }
529
+
530
+ class Form extends HTMLElement {
531
+ constructor({ mutators, extractors, valueHoldersSelector, ignoredChildrenSelector }) {
532
+ super();
533
+ Observable.mixin(this);
534
+ this.mutators = mutators || {};
535
+ this.extractors = extractors || {};
536
+ this.valueHoldersSelector = valueHoldersSelector || '[name]';
537
+ this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none';
538
+
539
+ const form = document.createElement('form');
540
+ form.append(...this.childNodes);
541
+ this.appendChild(form);
542
+
543
+ form.addEventListener('submit', async (e) => {
544
+ e.preventDefault();
545
+ this.spinner(true);
546
+ try {
547
+ await this.fire('submit', this.getValues(), this);
548
+ } catch (e) {
549
+ if (e instanceof Failure) {
550
+ this.setErrors(e.problems);
551
+ return;
552
+ }
553
+ throw e;
554
+ } finally {
555
+ this.spinner(false);
556
+ }
557
+ });
558
+ }
559
+ spinner(spin) {
560
+ this.querySelectorAll('ful-spinner').forEach(el => {
561
+ el[spin ? 'show' : 'hide']();
562
+ });
563
+ this.querySelectorAll('[type=submit],[type=reset]').forEach(el => {
564
+ el.disabled = spin;
565
+ });
566
+ }
567
+ setValues(values) {
568
+ for (let k in values) {
569
+ if (!values.hasOwnProperty(k)) {
570
+ continue;
571
+ }
572
+ Array.from(this.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
573
+ Form.mutate(this.mutators, el, values[k], k, values);
400
574
  });
401
575
  }
402
576
  }
577
+ getValues() {
578
+ return Array.from(this.querySelectorAll(this.valueHoldersSelector))
579
+ .filter((el) => {
580
+ if (el.dataset['fulBindInclude'] === 'never') {
581
+ return false;
582
+ }
583
+ return el.dataset['fulBindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
584
+ })
585
+ .reduce((result, el) => {
586
+ return Form.providePath(result, el.getAttribute('name'), Form.extract(this.extractors, el));
587
+ }, {});
588
+ }
589
+ setErrors(errors, scroll) {
590
+ this.clearErrors();
591
+ errors
592
+ .filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
593
+ .forEach((e) => {
594
+ const name = e.context.replace("[", ".").replace("].", ".");
595
+ this.querySelectorAll(`[name='${CSS.escape(name)}']`)
596
+ .forEach(input => {
597
+ input.classList.add('is-invalid');
598
+ if (input.parentElement.classList.contains("form-floating")) {
599
+ input.parentElement.classList.add('is-invalid');
600
+ }
601
+ });
602
+ this.querySelectorAll(`ful-field-error[field='${CSS.escape(name)}']`)
603
+ .forEach(el => el.innerText = e.reason);
604
+ });
605
+ this.querySelectorAll("ful-errors")
606
+ .forEach(el => {
607
+ const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
608
+ el.innerHTML = globalErrors.map(e => e.reason).join("\n");
609
+ if (globalErrors.length !== 0) {
610
+ el.classList.remove('d-none');
611
+ }
612
+ });
613
+
614
+ if (!scroll) {
615
+ return;
616
+ }
617
+ const ys = Array.from(this.querySelectorAll('ful-field-error:not(.d-none)'))
618
+ .map(el => el.getBoundingClientRect().y + window.scrollY);
619
+ const miny = Math.min(...ys);
620
+ if (miny !== Infinity) {
621
+ window.scroll(window.scrollX, miny > 100 ? miny - 100 : 0);
622
+ }
623
+ }
624
+ clearErrors() {
625
+ this.querySelectorAll('[name].is-invalid, .form-floating.is-invalid')
626
+ .forEach(el => el.classList.remove('is-invalid'));
627
+ this.querySelectorAll("ful-errors")
628
+ .forEach(el => {
629
+ el.innerHTML = '';
630
+ el.classList.add('d-none');
631
+ });
632
+ }
633
+ static extract(extractors, el) {
634
+ const maybeExtractor = extractors[el.dataset['fulBindExtractor']] || extractors[el.dataset['fulBindProvide']];
635
+ if (maybeExtractor) {
636
+ return maybeExtractor(el);
637
+ }
638
+ if (el.getAttribute('type') === 'radio') {
639
+ if (!el.checked) {
640
+ return undefined;
641
+ }
642
+ return el.dataset['fulBindType'] === 'boolean' ? el.value === 'true' : el.value;
643
+ }
644
+ if (el.getAttribute('type') === 'checkbox') {
645
+ return el.checked;
646
+ }
647
+ if (el.dataset['fulBindType'] === 'boolean') {
648
+ return !el.value ? null : el.value === 'true';
649
+ }
650
+ if (el.getValue) {
651
+ return el.getValue();
652
+ }
653
+ return el.value || null;
654
+ }
655
+ static mutate(mutators, el, raw, key, values) {
656
+ const maybeMutator = mutators[el.dataset['fulBindMutator']] || mutators[el.dataset['fulBindProvide']];
657
+ if (maybeMutator) {
658
+ maybeMutator(el, raw, key, values);
659
+ return;
660
+ }
661
+ if (el.getAttribute('type') === 'radio') {
662
+ el.checked = el.getAttribute('value') === raw;
663
+ return;
664
+ }
665
+ if (el.getAttribute('type') === 'checkbox') {
666
+ el.checked = raw;
667
+ return;
668
+ }
669
+ if (el.setValue) {
670
+ el.setValue(raw);
671
+ return;
672
+ }
673
+ el.value = raw;
674
+ }
675
+
676
+ static providePath(result, path, value) {
677
+ const keys = path.split(".").map((k) => k.match(/^[0-9]+$/) ? +k : k);
678
+ let current = result;
679
+ let previous = null;
680
+ for (let i = 0; ; ++i) {
681
+ const ckey = keys[i];
682
+ const pkey = keys[i - 1];
683
+ if (Number.isInteger(ckey) && !Array.isArray(current)) {
684
+ if (previous !== null) {
685
+ previous[pkey] = current = [];
686
+ } else {
687
+ result = current = [];
688
+ }
689
+ }
690
+ if (i === keys.length - 1) {
691
+ //when value is undefined we only want to define the property if it's not defined
692
+ current[ckey] = value !== undefined ? value : (ckey in current ? current[ckey] : null);
693
+ return result;
694
+ }
695
+ if (current[ckey] === undefined) {
696
+ current[ckey] = {};
697
+ }
698
+ previous = current;
699
+ current = current[ckey];
700
+ }
701
+ }
702
+ static custom(tagName, configuration) {
703
+ customElements.define(tagName, class extends Form {
704
+ constructor() {
705
+ super(configuration);
706
+ }
707
+ });
708
+ }
709
+ static configure(configuration) {
710
+ FieldError.configure();
711
+ Errors.configure();
712
+ Spinner.configure();
713
+ Input.configure();
714
+ Select.configure();
715
+ Form.custom('ful-form', configuration || {});
716
+ }
403
717
  }
404
718
 
405
719
  class Storage {
@@ -584,7 +898,7 @@ var ful = (function (exports) {
584
898
  ])
585
899
  });
586
900
  if (!response.ok) {
587
- throw new Error("Error:" + response.code + ": " + response.text());
901
+ throw new Error("Error:" + response.status + ": " + response.text());
588
902
  }
589
903
  const token = await response.json();
590
904
  this.token = token;
@@ -722,10 +1036,10 @@ var ful = (function (exports) {
722
1036
  }
723
1037
  };
724
1038
 
725
- class Wizard {
726
- constructor(el) {
727
- this.el = el;
728
- this.progress = [...el.children].filter(e => e.matches("header,ol,ul"));
1039
+ class Wizard extends HTMLElement {
1040
+ constructor() {
1041
+ super();
1042
+ this.progress = [...this.children].filter(e => e.matches("header,ol,ul"));
729
1043
 
730
1044
  this.progress.forEach(p => {
731
1045
  const children = [...p.children];
@@ -734,8 +1048,8 @@ var ful = (function (exports) {
734
1048
  children[0].classList.add('active');
735
1049
  }
736
1050
  });
737
- if (this.el.querySelector('section.current') === null) {
738
- const firstSection = this.el.querySelector('section:first-of-type');
1051
+ if (this.querySelector('section.current') === null) {
1052
+ const firstSection = this.querySelector('section:first-of-type');
739
1053
  if (firstSection !== null) {
740
1054
  firstSection.classList.add('current');
741
1055
  }
@@ -748,11 +1062,11 @@ var ful = (function (exports) {
748
1062
  current?.classList.remove('active');
749
1063
  current?.nextElementSibling?.classList.add('active');
750
1064
  });
751
- const currentSection = this.el.querySelector('section.current');
1065
+ const currentSection = this.querySelector('section.current');
752
1066
  currentSection.classList.remove("current");
753
1067
  currentSection.nextElementSibling.classList.add('current');
754
1068
 
755
- this.el.dispatchEvent(new CustomEvent('wizard:activate', {
1069
+ this.dispatchEvent(new CustomEvent('wizard:activate', {
756
1070
  bubbles: true,
757
1071
  cancelable: true
758
1072
  }));
@@ -765,10 +1079,10 @@ var ful = (function (exports) {
765
1079
  current?.classList.remove('active');
766
1080
  current?.previousElementSibling?.classList.add('active');
767
1081
  });
768
- const currentSection = this.el.querySelector('section.current');
1082
+ const currentSection = this.querySelector('section.current');
769
1083
  currentSection.classList.remove("current");
770
1084
  currentSection.previousElementSibling.classList.add('current');
771
- this.el.dispatchEvent(new CustomEvent('wizard:activate', {
1085
+ this.dispatchEvent(new CustomEvent('wizard:activate', {
772
1086
  bubbles: true,
773
1087
  cancelable: true
774
1088
  }));
@@ -780,15 +1094,25 @@ var ful = (function (exports) {
780
1094
  current?.classList.remove('active');
781
1095
  p.children[+n]?.classList.add('active');
782
1096
  });
783
- const currentSection = this.el.querySelector('section.current');
1097
+ const currentSection = this.querySelector('section.current');
784
1098
  currentSection?.classList.remove("current");
785
- const nthSection = this.el.querySelector(`section:nth-child(${+n})`);
1099
+ const nthSection = this.querySelector(`section:nth-child(${+n})`);
786
1100
  nthSection.classList.add('current');
787
- this.el.dispatchEvent(new CustomEvent('wizard:activate', {
1101
+ this.dispatchEvent(new CustomEvent('wizard:activate', {
788
1102
  bubbles: true,
789
1103
  cancelable: true
790
1104
  }));
791
1105
  }
1106
+ static custom(tagName, configuration) {
1107
+ customElements.define(tagName, class extends Wizard {
1108
+ constructor() {
1109
+ super(configuration);
1110
+ }
1111
+ });
1112
+ }
1113
+ static configure() {
1114
+ return Wizard.custom('ful-wizard');
1115
+ }
792
1116
  }
793
1117
 
794
1118
  class App {
@@ -822,15 +1146,22 @@ var ful = (function (exports) {
822
1146
  exports.AuthorizationCodeFlowInterceptor = AuthorizationCodeFlowInterceptor;
823
1147
  exports.AuthorizationCodeFlowSession = AuthorizationCodeFlowSession;
824
1148
  exports.Base64 = Base64;
825
- exports.Bindings = Bindings;
1149
+ exports.CustomElements = CustomElements;
1150
+ exports.Errors = Errors;
826
1151
  exports.Failure = Failure;
1152
+ exports.FieldError = FieldError;
827
1153
  exports.Form = Form;
828
1154
  exports.Hex = Hex;
829
1155
  exports.HttpClient = HttpClient;
1156
+ exports.Input = Input;
830
1157
  exports.LocalStorage = LocalStorage;
1158
+ exports.Observable = Observable;
1159
+ exports.Select = Select;
831
1160
  exports.SessionStorage = SessionStorage;
1161
+ exports.Spinner = Spinner;
832
1162
  exports.VersionedStorage = VersionedStorage;
833
1163
  exports.Wizard = Wizard;
1164
+ exports.jsonRequest = jsonRequest;
834
1165
  exports.timing = timing;
835
1166
 
836
1167
  Object.defineProperty(exports, '__esModule', { value: true });