@optionfactory/ful 0.49.0 → 0.51.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.mjs CHANGED
@@ -562,7 +562,7 @@ const timing = {
562
562
  class SyncEvent extends CustomEvent {
563
563
  #results;
564
564
  constructor(type, options) {
565
- super(type, options);
565
+ super(type, {...options, cancelable: true});
566
566
  this.#results = [];
567
567
  }
568
568
 
@@ -571,9 +571,11 @@ class SyncEvent extends CustomEvent {
571
571
  // event handlers asynchronously via the event loop, dispatchEvent()
572
572
  // invokes event handlers synchronously.
573
573
  // see: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent
574
- const success = el.dispatchEvent(this);
574
+ el.dispatchEvent(this);
575
+ //we ignore the result of dispatchEvent and use defaultPrevented instead
576
+ //because handlers can be async
575
577
  const results = await Promise.all(this.#results);
576
- return [success, results];
578
+ return [!this.defaultPrevented, results];
577
579
  }
578
580
 
579
581
  static on(el, type, h, useCapture) {
@@ -585,6 +587,11 @@ class SyncEvent extends CustomEvent {
585
587
  }
586
588
 
587
589
  class Fragments {
590
+ /**
591
+ *
592
+ * @param {...string} html
593
+ * @returns
594
+ */
588
595
  static fromHtml(...html) {
589
596
  const el = document.createElement("div");
590
597
  el.innerHTML = html.join("");
@@ -592,16 +599,31 @@ class Fragments {
592
599
  fragment.append(...el.childNodes);
593
600
  return fragment;
594
601
  }
602
+ /**
603
+ *
604
+ * @param {DocumentFragment} fragment
605
+ * @returns
606
+ */
595
607
  static toHtml(fragment) {
596
608
  var r = document.createElement("div");
597
609
  r.appendChild(fragment);
598
610
  return r.innerHTML;
599
611
  }
612
+ /**
613
+ *
614
+ * @param {...Node} nodes
615
+ * @returns
616
+ */
600
617
  static from(...nodes) {
601
618
  const fragment = new DocumentFragment();
602
619
  fragment.append(...nodes);
603
620
  return fragment;
604
621
  }
622
+ /**
623
+ *
624
+ * @param {HTMLElement} el
625
+ * @returns
626
+ */
605
627
  static fromChildNodes(el) {
606
628
  const fragment = new DocumentFragment();
607
629
  fragment.append(...el.childNodes);
@@ -611,18 +633,41 @@ class Fragments {
611
633
 
612
634
  class Attributes {
613
635
  static id = 0;
636
+ /**
637
+ *
638
+ * @param {string} prefix
639
+ * @returns
640
+ */
614
641
  static uid(prefix) {
615
642
  return `${prefix}-${++Attributes.id}`;
616
643
  }
644
+ /**
645
+ *
646
+ * @param {any} value
647
+ * @returns
648
+ */
617
649
  static asBoolean(value) {
618
650
  return value !== null && value !== undefined && value !== false;
619
651
  }
652
+ /**
653
+ *
654
+ * @param {HTMLElement} el
655
+ * @param {string} k
656
+ * @param {string} v
657
+ * @returns
658
+ */
620
659
  static defaultValue(el, k, v) {
621
660
  if (!el.hasAttribute(k)) {
622
661
  el.setAttribute(k, v);
623
662
  }
624
663
  return el.getAttribute(k);
625
664
  }
665
+ /**
666
+ *
667
+ * @param {string} prefix
668
+ * @param {HTMLElement} from
669
+ * @param {HTMLElement} to
670
+ */
626
671
  static forward(prefix, from, to) {
627
672
  from.getAttributeNames()
628
673
  .filter(a => a.startsWith(prefix))
@@ -637,7 +682,12 @@ class Attributes {
637
682
  }
638
683
  }
639
684
 
640
- class Slots {
685
+ class LightSlots {
686
+ /**
687
+ *
688
+ * @param {HTMLElement} el
689
+ * @returns the slots
690
+ */
641
691
  static from(el) {
642
692
  const namedSlots = Array.from(el.childNodes)
643
693
  .filter(el => el.matches && el.matches('[slot]'))
@@ -647,10 +697,10 @@ class Slots {
647
697
  el.removeAttribute("slot");
648
698
  return [slot, el];
649
699
  });
650
- const slotted = Object.fromEntries(namedSlots);
651
- slotted.default = new DocumentFragment();
652
- slotted.default.append(...el.childNodes);
653
- return slotted;
700
+ const slots = Object.fromEntries(namedSlots);
701
+ slots.default = new DocumentFragment();
702
+ slots.default.append(...el.childNodes);
703
+ return slots;
654
704
  }
655
705
  }
656
706
 
@@ -665,20 +715,20 @@ class Nodes {
665
715
  }
666
716
  }
667
717
 
668
- class TemplateRegistry {
718
+ class TemplatesRegistry {
669
719
  #idToFragment = {};
670
720
  #idToTemplate = {};
671
721
  #ec;
672
722
  put(k, fragment) {
673
723
  if (this.#ec) {
674
- this.#idToTemplate[k] = ftl.Template.fromFragment(fragment, ec);
724
+ this.#idToTemplate[k] = Template.fromFragment(fragment, ec);
675
725
  return;
676
726
  }
677
727
  this.#idToFragment[k] = fragment;
678
728
  }
679
729
  get(k) {
680
730
  if (!this.#ec) {
681
- throw new Error("evaluationContext is not configured");
731
+ throw new Error("TemplatesRegistry is not configured");
682
732
  }
683
733
  const tpl = this.#idToTemplate[k];
684
734
  if (!tpl) {
@@ -695,7 +745,50 @@ class TemplateRegistry {
695
745
  }
696
746
  }
697
747
 
698
- const templates = new TemplateRegistry();
748
+
749
+ class ElementsRegistry {
750
+ #templates;
751
+ #tagToclass;
752
+ #configured;
753
+ #id = 0;
754
+ constructor(){
755
+ this.#templates = new TemplatesRegistry();
756
+ this.#tagToclass = {};
757
+ }
758
+ defineTemplate(html){
759
+ if(html === null || html === undefined){
760
+ return undefined;
761
+ }
762
+ const name = `unnamed-${++this.#id}`;
763
+ this.#templates.put(name, Fragments.fromHtml(html));
764
+ return name;
765
+ }
766
+ template(k){
767
+ if(k === null || k === undefined){
768
+ return undefined;
769
+ }
770
+ return this.#templates.get(k);
771
+ }
772
+ define(tag, klass){
773
+ if(!this.#configured){
774
+ this.#tagToclass[tag] = klass;
775
+ return this;
776
+ }
777
+ customElements.define(tag, klass);
778
+ return this;
779
+ }
780
+ configure(ec) {
781
+ this.#templates.configure(ec);
782
+ for(const [tag, klass] of Object.entries(this.#tagToclass)) {
783
+ customElements.define(tag, klass);
784
+ delete this.#tagToclass[tag];
785
+ }
786
+ this.#configured = true;
787
+ }
788
+ }
789
+
790
+ const elements = new ElementsRegistry();
791
+
699
792
 
700
793
  class UpgradeQueue {
701
794
  #q = [];
@@ -715,22 +808,29 @@ class UpgradeQueue {
715
808
 
716
809
  const upgradeQueue = new UpgradeQueue();
717
810
 
718
- const ParsedElement = (flags, others) => {
811
+ const ParsedElement = (conf) => {
812
+ const {flags, attrs, template, slots} = conf || {};
719
813
 
720
814
  const observed_flags = flags || [];
721
- const observed_others = others || [];
815
+ const observed_others = attrs || [];
722
816
  const observed = [].concat(observed_flags).concat(observed_others);
723
817
 
818
+ const templateId = elements.defineTemplate(template);
819
+
724
820
  const k = class extends HTMLElement {
725
821
  static get observedAttributes() {
726
822
  return observed;
727
823
  }
728
824
  #parsed;
825
+ #initialized;
729
826
  #internals;
730
827
  constructor(...args) {
731
828
  super(...args);
732
829
  this.#internals = this.attachInternals();
733
830
  }
831
+ get initialized(){
832
+ return this.#initialized;
833
+ }
734
834
  get internals() {
735
835
  return this.#internals;
736
836
  }
@@ -766,34 +866,50 @@ const ParsedElement = (flags, others) => {
766
866
  return;
767
867
  }
768
868
  this.#parsed = true;
769
- await this.render();
869
+ await this.render(elements.template(templateId), slots ? LightSlots.from(this) : undefined);
770
870
  for (const flag of observed_flags) {
771
- if(this.hasAttribute(flag)){
871
+ if (this.hasAttribute(flag)) {
772
872
  this[flag] = true;
773
873
  }
774
874
  }
775
875
  for (const other of observed_others) {
776
- if(this.hasAttribute(other)){
876
+ if (this.hasAttribute(other)) {
777
877
  this[other] = this.getAttribute(other);
778
878
  }
779
879
  }
780
- }
880
+ this.#initialized = true;
881
+ }
781
882
  };
782
883
 
783
884
  for (const flag of observed_flags) {
885
+ const state = `--${flag};`;
784
886
  Object.defineProperty(k.prototype, flag, {
785
887
  enumerable: true,
786
888
  configurable: true,
787
889
  get() {
788
- return this.internals.states.has(`--${flag}`);
890
+ return this.internals.states.has(state);
789
891
  },
790
892
  set(value) {
791
- //see https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet#using_double_dash_prefixed_idents
792
- if (Attributes.asBoolean(value)) {
793
- this.internals.states.add(`--${flag}`);
794
- return;
795
- }
796
- this.internals.states.delete(`--${flag}`);
893
+ const v = Attributes.asBoolean(value);
894
+ const et = this.initialized ? 'changed' : 'init';
895
+ const event = new SyncEvent(`${flag}:${et}`, {
896
+ detail: {
897
+ target: this,
898
+ value: v
899
+ }
900
+ });
901
+ (async () => {
902
+ const [success, results] = await event.dispatchTo(this);
903
+ if (!success) {
904
+ return;
905
+ }
906
+ if (v) {
907
+ //see https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet#using_double_dash_prefixed_idents
908
+ this.internals.states.add(state);
909
+ return;
910
+ }
911
+ this.internals.states.delete(state);
912
+ })();
797
913
  }
798
914
  });
799
915
  }
@@ -950,21 +1066,20 @@ class Form extends ParsedElement() {
950
1066
  }
951
1067
  }
952
1068
 
953
- templates.put('ful-input', Fragments.fromHtml(`
954
- <label data-tpl-for="id" class="form-label">{{{{ slotted.default }}}}</label>
955
- <div class="input-group">
956
- <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
957
- <div data-tpl-if="slotted.before" data-tpl-remove="tag">{{{{ slotted.before }}}}</div>
958
- {{{{ slotted.input }}}}
959
- <div data-tpl-if="slotted.after" data-tpl-remove="tag">{{{{ slotted.after }}}}</div>
960
- <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
961
- </div>
962
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
963
- `));
964
-
965
-
966
- const makeInputFragment = (el, slotted) => {
967
- const input = el.input = slotted.input = slotted.input || (() => {
1069
+ const INPUT_TEMPLATE = `
1070
+ <label data-tpl-for="id" class="form-label">{{{{ slots.default }}}}</label>
1071
+ <div class="input-group">
1072
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
1073
+ <div data-tpl-if="slots.before" data-tpl-remove="tag">{{{{ slots.before }}}}</div>
1074
+ {{{{ slots.input }}}}
1075
+ <div data-tpl-if="slots.after" data-tpl-remove="tag">{{{{ slots.after }}}}</div>
1076
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
1077
+ </div>
1078
+ <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1079
+ `;
1080
+
1081
+ const makeInputFragment = (el, template, slots) => {
1082
+ const input = el.input = slots.input = slots.input || (() => {
968
1083
  const el = document.createElement("input");
969
1084
  el.classList.add("form-control");
970
1085
  return el;
@@ -972,18 +1087,22 @@ const makeInputFragment = (el, slotted) => {
972
1087
  input.setAttribute('ful-validation-target', '');
973
1088
 
974
1089
  const id = input.getAttribute('id') || el.getAttribute('input-id') || Attributes.uid('ful-input');
975
- Attributes.forward('input-', el, slotted.input);
976
- Attributes.defaultValue(slotted.input, "id", id);
977
- Attributes.defaultValue(slotted.input, "type", "text");
978
- Attributes.defaultValue(slotted.input, "placeholder", " ");
1090
+ Attributes.forward('input-', el, slots.input);
1091
+ Attributes.defaultValue(slots.input, "id", id);
1092
+ Attributes.defaultValue(slots.input, "type", "text");
1093
+ Attributes.defaultValue(slots.input, "placeholder", " ");
979
1094
  const name = el.getAttribute('name');
980
- return templates.get('ful-input').render(el, { id, name, slotted });
1095
+ return template.render(el, { id, name, slots });
981
1096
  };
982
1097
 
983
- class Input extends ParsedElement([], ['value']) {
984
- render() {
985
- const slotted = Slots.from(this);
986
- const fragment = makeInputFragment(this, slotted);
1098
+ class Input extends ParsedElement({
1099
+ flags: [],
1100
+ attrs: ['value'],
1101
+ slots: true,
1102
+ template: INPUT_TEMPLATE
1103
+ }){
1104
+ render(template, slots) {
1105
+ const fragment = makeInputFragment(this, template, slots);
987
1106
  this.replaceChildren(fragment);
988
1107
  }
989
1108
  get value() {
@@ -999,33 +1118,33 @@ class Input extends ParsedElement([], ['value']) {
999
1118
  * <link href="tom-select.bootstrap5.css" rel="stylesheet" />
1000
1119
  */
1001
1120
 
1002
- templates.put('ful-select', Fragments.fromHtml(`
1003
- <label data-tpl-for="tsId" class="form-label">{{{{ slotted.default }}}}</label>
1004
- {{{{ input }}}}
1005
- <div class="input-group">
1006
- <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
1007
- <div data-tpl-if="slotted.before" data-tpl-remove="tag">{{{{ slotted.before }}}}</div>
1008
- {{{{ slotted.input }}}}
1009
- <div data-tpl-if="slotted.after" data-tpl-remove="tag">{{{{ slotted.after }}}}</div>
1010
- <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
1011
- </div>
1012
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1013
- `));
1014
-
1015
-
1016
- class Select extends ParsedElement([], ["value"]) {
1121
+ class Select extends ParsedElement({
1122
+ flags: [],
1123
+ attrs: ["value"],
1124
+ slots: true,
1125
+ template: `
1126
+ <label data-tpl-for="tsId" class="form-label">{{{{ slots.default }}}}</label>
1127
+ {{{{ input }}}}
1128
+ <div class="input-group">
1129
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
1130
+ <div data-tpl-if="slots.before" data-tpl-remove="tag">{{{{ slots.before }}}}</div>
1131
+ {{{{ slots.input }}}}
1132
+ <div data-tpl-if="slots.after" data-tpl-remove="tag">{{{{ slots.after }}}}</div>
1133
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
1134
+ </div>
1135
+ <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1136
+ `
1137
+ }) {
1017
1138
  constructor(tsConfig) {
1018
1139
  super();
1019
1140
  this.tsConfig = tsConfig;
1020
1141
  }
1021
- render() {
1022
- const slotted = Slots.from(this);
1023
-
1142
+ render(template, slots) {
1024
1143
  const type = this.getAttribute("type") || 'local';
1025
1144
  const remote = type != 'local';
1026
1145
  const loadOnce = this.getAttribute('load') != 'always';
1027
1146
  const name = this.getAttribute('name');
1028
- const input = slotted.input = slotted.input || (() => {
1147
+ const input = slots.input = slots.input || (() => {
1029
1148
  return document.createElement("select");
1030
1149
  })();
1031
1150
  input.setAttribute('ful-validation-target', '');
@@ -1038,7 +1157,7 @@ class Select extends ParsedElement([], ["value"]) {
1038
1157
 
1039
1158
  //tomselect needs the input to have a parent.
1040
1159
  //se we move the input to a fragment
1041
- slotted.input = Fragments.from(input);
1160
+ slots.input = Fragments.from(input);
1042
1161
 
1043
1162
  this.loaded = !remote;
1044
1163
 
@@ -1061,7 +1180,7 @@ class Select extends ParsedElement([], ["value"]) {
1061
1180
  }
1062
1181
  const type = query && query.hasOwnProperty('byId') ? 'id' : 'query';
1063
1182
  const qvalue = type === 'id' ? query.byId : query;
1064
- const data = await (this.loader ? this.loader(qvalue, type) : []);
1183
+ const data = await (this.#loader ? this.#loader(qvalue, type) : []);
1065
1184
  if (type !== 'id') {
1066
1185
  this.loaded = true;
1067
1186
  }
@@ -1076,7 +1195,15 @@ class Select extends ParsedElement([], ["value"]) {
1076
1195
  } : {}, tsDefaultConfig, this.tsConfig));
1077
1196
  //we remove the input to move it
1078
1197
  input.remove();
1079
- templates.get('ful-select').renderTo(this, { id, tsId, name, input, slotted });
1198
+ template.renderTo(this, { id, tsId, name, input, slots });
1199
+ }
1200
+ #loader;
1201
+ set loader(l){
1202
+ this.#loader = l;
1203
+ // loader can be configured later so we load now
1204
+ if(this.hasAttribute('value')){
1205
+ this.value = this.getAttribute("value");
1206
+ }
1080
1207
  }
1081
1208
  set value(v) {
1082
1209
  (async () => {
@@ -1092,33 +1219,34 @@ class Select extends ParsedElement([], ["value"]) {
1092
1219
  }
1093
1220
  }
1094
1221
 
1095
- templates.put('ful-radio-group', Fragments.fromHtml(`
1096
- <fieldset>
1097
- <legend class="form-label">
1098
- {{{{ slotted.default }}}}
1099
- </legend>
1100
- <header data-tpl-if="slotted.header">
1101
- {{{{ slotted.header }}}}
1102
- </header>
1103
- <section>
1104
- <label data-tpl-each="inputsAndLabels" data-tpl-var="ial">
1105
- {{{{ ial[0] }}}}
1106
- {{{{ ial[1] }}}}
1107
- </label>
1108
- </section>
1109
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1110
- <footer data-tpl-if="slotted.footer">
1111
- {{{{ slotted.footer }}}}
1112
- </footer>
1113
- </fieldset>
1114
- `));
1115
-
1116
-
1117
- class RadioGroup extends ParsedElement(['disabled'], ['value']) {
1118
- render() {
1119
- const slotted = Slots.from(this);
1222
+ class RadioGroup extends ParsedElement({
1223
+ flags: ['disabled'],
1224
+ attrs: ['value'],
1225
+ slots: true,
1226
+ template: `
1227
+ <fieldset>
1228
+ <legend class="form-label">
1229
+ {{{{ slots.default }}}}
1230
+ </legend>
1231
+ <header data-tpl-if="slots.header">
1232
+ {{{{ slots.header }}}}
1233
+ </header>
1234
+ <section>
1235
+ <label data-tpl-each="inputsAndLabels" data-tpl-var="ial">
1236
+ {{{{ ial[0] }}}}
1237
+ {{{{ ial[1] }}}}
1238
+ </label>
1239
+ </section>
1240
+ <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1241
+ <footer data-tpl-if="slots.footer">
1242
+ {{{{ slots.footer }}}}
1243
+ </footer>
1244
+ </fieldset>
1245
+ `
1246
+ }) {
1247
+ render(template, slots) {
1120
1248
  const name = this.getAttribute('name') || Attributes.uid('ful-radiogroup');
1121
- const radioEls = Array.from(slotted.default.querySelectorAll('ful-radio'));
1249
+ const radioEls = Array.from(slots.default.querySelectorAll('ful-radio'));
1122
1250
  const inputsAndLabels = radioEls.map(el => {
1123
1251
  const input = document.createElement('input');
1124
1252
  input.setAttribute('type', 'radio');
@@ -1131,11 +1259,7 @@ class RadioGroup extends ParsedElement(['disabled'], ['value']) {
1131
1259
  return [input, label];
1132
1260
  });
1133
1261
  radioEls.forEach(el => el.remove());
1134
- templates.get('ful-radio-group').renderTo(this, {
1135
- name: name,
1136
- slotted: slotted,
1137
- inputsAndLabels: inputsAndLabels
1138
- });
1262
+ template.renderTo(this, {name, slots, inputsAndLabels});
1139
1263
  }
1140
1264
  get value() {
1141
1265
  const checked = this.querySelector('input[type=radio]:checked');
@@ -1146,17 +1270,17 @@ class RadioGroup extends ParsedElement(['disabled'], ['value']) {
1146
1270
  }
1147
1271
  }
1148
1272
 
1149
- templates.put('ful-spinner', Fragments.fromHtml(`
1150
- <div class="ful-spinner-wrapper">
1151
- <div class="ful-spinner-text">{{{{ slotted.default }}}}</div>
1152
- <div class="ful-spinner-icon"></div>
1153
- </div>
1154
- `));
1155
-
1156
- class Spinner extends ParsedElement() {
1157
- render() {
1158
- const slotted = Slots.from(this);
1159
- templates.get('ful-spinner').renderTo(this, { slotted });
1273
+ class Spinner extends ParsedElement({
1274
+ slots: true,
1275
+ template: `
1276
+ <div class="ful-spinner-wrapper">
1277
+ <div class="ful-spinner-text">{{{{ slots.default }}}}</div>
1278
+ <div class="ful-spinner-icon"></div>
1279
+ </div>
1280
+ `
1281
+ }) {
1282
+ render(template, slots) {
1283
+ template.renderTo(this, { slots });
1160
1284
  }
1161
1285
  }
1162
1286
 
@@ -1232,16 +1356,5 @@ class Wizard extends HTMLElement {
1232
1356
  }
1233
1357
  }
1234
1358
 
1235
- class CustomElements {
1236
- static configure(ec) {
1237
- templates.configure(ec);
1238
- customElements.define('ful-spinner', Spinner);
1239
- customElements.define('ful-input', Input);
1240
- customElements.define('ful-radio-group', RadioGroup);
1241
- customElements.define('ful-select', Select);
1242
- customElements.define('ful-form', Form);
1243
- }
1244
- }
1245
-
1246
- export { Attributes, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, CustomElements, Failure, Form, Fragments, Hex, HttpClient, Input, LocalStorage, Nodes, ParsedElement, RadioGroup, Select, SessionStorage, Slots, Spinner, SyncEvent, TemplateRegistry, VersionedStorage, Wizard, jsonPatch, jsonPost, jsonPut, jsonRequest, makeInputFragment, templates, timing };
1359
+ export { Attributes, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, ElementsRegistry, Failure, Form, Fragments, Hex, HttpClient, INPUT_TEMPLATE, Input, LightSlots, LocalStorage, Nodes, ParsedElement, RadioGroup, Select, SessionStorage, Spinner, SyncEvent, TemplatesRegistry, VersionedStorage, Wizard, elements, jsonPatch, jsonPost, jsonPut, jsonRequest, makeInputFragment, timing };
1247
1360
  //# sourceMappingURL=ful.mjs.map