@aemforms/af-core 0.22.73 → 0.22.75

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.
@@ -1,4 +1,4 @@
1
- import { propertyChange, ExecuteRule, Initialize, RemoveItem, SubmitSuccess, CustomEvent, SubmitFailure, Submit, RemoveInstance, AddInstance, Reset, AddItem, Click, Change, FormLoad, FieldChanged, ValidationComplete, Valid, Invalid } from './afb-events.js';
1
+ import { propertyChange, ExecuteRule, Initialize, RemoveItem, SubmitSuccess, CustomEvent, SubmitError, SubmitFailure, Submit, RemoveInstance, AddInstance, Reset, AddItem, Click, Change, FormLoad, FieldChanged, ValidationComplete, Valid, Invalid } from './afb-events.js';
2
2
  import Formula from '@adobe/json-formula';
3
3
  import { parseDefaultDate, datetimeToNumber, format, parseDateSkeleton, formatDate, numberToDatetime } from '@aemforms/af-formatters';
4
4
 
@@ -578,593 +578,664 @@ const resolveData = (data, input, create) => {
578
578
  return result;
579
579
  };
580
580
 
581
- const editableProperties = [
582
- 'value',
583
- 'label',
584
- 'description',
585
- 'visible',
586
- 'enabled',
587
- 'valid',
588
- 'errorMessage',
589
- 'readOnly',
590
- 'enum',
591
- 'enumNames',
592
- 'required',
593
- 'properties',
594
- 'exclusiveMinimum',
595
- 'exclusiveMaximum',
596
- 'maximum',
597
- 'maxItems',
598
- 'minimum',
599
- 'minItems'
600
- ];
601
- const dynamicProps = [
602
- ...editableProperties,
603
- 'index',
604
- 'activeChild'
605
- ];
606
- const staticFields = ['plain-text', 'image'];
607
- class ActionImplWithTarget {
608
- _action;
609
- _target;
610
- constructor(_action, _target) {
611
- this._action = _action;
612
- this._target = _target;
581
+ class FileObject {
582
+ data;
583
+ mediaType = 'application/octet-stream';
584
+ name = 'unknown';
585
+ size = 0;
586
+ constructor(init) {
587
+ Object.assign(this, init);
613
588
  }
614
589
  get type() {
615
- return this._action.type;
616
- }
617
- get payload() {
618
- return this._action.payload;
619
- }
620
- get metadata() {
621
- return this._action.metadata;
622
- }
623
- get target() {
624
- return this._target;
625
- }
626
- get isCustomEvent() {
627
- return this._action.isCustomEvent;
590
+ return this.mediaType;
628
591
  }
629
- get originalAction() {
630
- return this._action.originalAction;
592
+ toJSON() {
593
+ return {
594
+ 'name': this.name,
595
+ 'size': this.size,
596
+ 'mediaType': this.mediaType,
597
+ 'data': this.data.toString()
598
+ };
631
599
  }
632
- toString() {
633
- return this._action.toString();
600
+ equals(obj) {
601
+ return (this.data === obj.data &&
602
+ this.mediaType === obj.mediaType &&
603
+ this.name === obj.name &&
604
+ this.size === obj.size);
634
605
  }
635
606
  }
636
- const target = Symbol('target');
637
- const qualifiedName = Symbol('qualifiedName');
638
- function dependencyTracked() {
639
- return function (target, propertyKey, descriptor) {
640
- const get = descriptor.get;
641
- if (get != undefined) {
642
- descriptor.get = function () {
643
- this.ruleEngine.trackDependency(this);
644
- return get.call(this);
645
- };
607
+
608
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'.split('');
609
+ const fileSizeRegex = /^(\d*\.?\d+)(\\?(?=[KMGT])([KMGT])(?:i?B)?|B?)$/i;
610
+ const randomWord = (l) => {
611
+ const ret = [];
612
+ for (let i = 0; i <= l; i++) {
613
+ let randIndex;
614
+ if (i === 0) {
615
+ randIndex = Math.floor(Math.random() * (chars.length - 11));
646
616
  }
647
- };
648
- }
649
- const addOnly = (includeOrExclude) => (...fieldTypes) => (target, propertyKey, descriptor) => {
650
- const get = descriptor.get;
651
- if (get != undefined) {
652
- descriptor.get = function () {
653
- if (fieldTypes.indexOf(this.fieldType) > -1 === includeOrExclude) {
654
- return get.call(this);
655
- }
656
- return undefined;
657
- };
658
- }
659
- const set = descriptor.set;
660
- if (set != undefined) {
661
- descriptor.set = function (value) {
662
- if (fieldTypes.indexOf(this.fieldType) > -1 === includeOrExclude) {
663
- set.call(this, value);
664
- }
665
- };
617
+ else {
618
+ randIndex = Math.floor(Math.random() * (chars.length));
619
+ }
620
+ ret.push(chars[randIndex]);
666
621
  }
622
+ return ret.join('');
667
623
  };
668
- const include = addOnly(true);
669
- const exclude = addOnly(false);
670
- class BaseNode {
671
- _options;
672
- _ruleNode;
673
- _lang = '';
674
- _callbacks = {};
675
- _dependents = [];
676
- _jsonModel;
677
- _tokens = [];
678
- get isContainer() {
679
- return false;
680
- }
681
- constructor(params, _options) {
682
- this._options = _options;
683
- this[qualifiedName] = null;
684
- this._jsonModel = {
685
- ...params,
686
- id: 'id' in params ? params.id : this.form.getUniqueId()
687
- };
688
- }
689
- setupRuleNode() {
690
- const self = this;
691
- this._ruleNode = new Proxy(this.ruleNodeReference(), {
692
- get: (ruleNodeReference, prop) => {
693
- return self.getFromRule(ruleNodeReference, prop);
694
- }
695
- });
696
- }
697
- ruleNodeReference() {
698
- return this;
699
- }
700
- getRuleNode() {
701
- return this._ruleNode;
702
- }
703
- getFromRule(ruleNodeReference, prop) {
704
- if (prop === Symbol.toPrimitive || (prop === 'valueOf' && !ruleNodeReference.hasOwnProperty('valueOf'))) {
705
- return this.valueOf;
624
+ const getAttachments = (input, excludeUnbound = false) => {
625
+ const items = input.items || [];
626
+ return items?.reduce((acc, item) => {
627
+ if (excludeUnbound && item.dataRef === null) {
628
+ return acc;
706
629
  }
707
- else if (prop === target) {
708
- return this;
630
+ let ret = null;
631
+ if (item.isContainer) {
632
+ ret = getAttachments(item, excludeUnbound);
709
633
  }
710
- else if (typeof (prop) === 'string') {
711
- if (prop.startsWith('$')) {
712
- prop = prop.substr(1);
713
- if (typeof this[prop] !== 'function') {
714
- const retValue = this[prop];
715
- if (retValue instanceof BaseNode) {
716
- return retValue.getRuleNode();
717
- }
718
- else if (retValue instanceof Array) {
719
- return retValue.map(r => r instanceof BaseNode ? r.getRuleNode() : r);
720
- }
721
- else {
722
- return retValue;
723
- }
724
- }
725
- }
726
- else {
727
- if (ruleNodeReference.hasOwnProperty(prop)) {
728
- return ruleNodeReference[prop];
634
+ else {
635
+ if (isFile(item.getState())) {
636
+ ret = {};
637
+ const name = item.name || '';
638
+ const dataRef = (item.dataRef != null)
639
+ ? item.dataRef
640
+ : (name.length > 0 ? item.name : undefined);
641
+ if (item.value instanceof Array) {
642
+ ret[item.id] = item.value.map((x) => {
643
+ return { ...x, 'dataRef': dataRef };
644
+ });
729
645
  }
730
- else if (typeof ruleNodeReference[prop] === 'function') {
731
- return ruleNodeReference[prop];
646
+ else if (item.value != null) {
647
+ ret[item.id] = { ...item.value, 'dataRef': dataRef };
732
648
  }
733
649
  }
734
650
  }
735
- }
736
- get id() {
737
- return this._jsonModel.id;
738
- }
739
- get index() {
740
- if (this.parent) {
741
- return this.parent.indexOf(this);
651
+ return Object.assign(acc, ret);
652
+ }, {});
653
+ };
654
+ const getFileSizeInBytes = (str) => {
655
+ let retVal = 0;
656
+ if (typeof str === 'string') {
657
+ const matches = fileSizeRegex.exec(str.trim());
658
+ if (matches != null) {
659
+ retVal = sizeToBytes(parseFloat(matches[1]), (matches[2] || 'kb').toUpperCase());
742
660
  }
743
- return 0;
744
- }
745
- get parent() {
746
- return this._options.parent;
747
- }
748
- get type() {
749
- return this._jsonModel.type;
750
- }
751
- get repeatable() {
752
- return this.parent?.hasDynamicItems();
753
- }
754
- get fieldType() {
755
- return this._jsonModel.fieldType || 'text-input';
756
- }
757
- get ':type'() {
758
- return this._jsonModel[':type'] || this.fieldType;
759
- }
760
- get name() {
761
- return this._jsonModel.name;
762
- }
763
- get description() {
764
- return this._jsonModel.description;
765
- }
766
- set description(d) {
767
- this._setProperty('description', d);
768
- }
769
- get dataRef() {
770
- return this._jsonModel.dataRef;
771
661
  }
772
- get visible() {
773
- if (this.parent?.visible !== undefined) {
774
- return this.parent?.visible ? this._jsonModel.visible : false;
775
- }
776
- else {
777
- return this._jsonModel.visible;
662
+ return retVal;
663
+ };
664
+ const sizeToBytes = (size, symbol) => {
665
+ const sizes = { 'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4 };
666
+ const i = Math.pow(1024, sizes[symbol]);
667
+ return Math.round(size * i);
668
+ };
669
+ const IdGenerator = function* (initial = 50) {
670
+ const initialize = function () {
671
+ const arr = [];
672
+ for (let i = 0; i < initial; i++) {
673
+ arr.push(randomWord(10));
778
674
  }
779
- }
780
- set visible(v) {
781
- if (v !== this._jsonModel.visible) {
782
- const changeAction = propertyChange('visible', v, this._jsonModel.visible);
783
- this._jsonModel.visible = v;
784
- this.notifyDependents(changeAction);
675
+ return arr;
676
+ };
677
+ const passedIds = {};
678
+ let ids = initialize();
679
+ do {
680
+ let x = ids.pop();
681
+ while (x in passedIds) {
682
+ if (ids.length === 0) {
683
+ ids = initialize();
684
+ }
685
+ x = ids.pop();
686
+ }
687
+ passedIds[x] = true;
688
+ yield ids.pop();
689
+ if (ids.length === 0) {
690
+ ids = initialize();
691
+ }
692
+ } while (ids.length > 0);
693
+ };
694
+ const isDataUrl = (str) => {
695
+ const dataUrlRegex = /^data:([a-z]+\/[a-z0-9-+.]+)?;(?:name=(.*);)?base64,(.*)$/;
696
+ return dataUrlRegex.exec(str.trim()) != null;
697
+ };
698
+ const extractFileInfo = (file) => {
699
+ if (file !== null) {
700
+ let retVal = null;
701
+ if (file instanceof FileObject) {
702
+ retVal = file;
703
+ }
704
+ else if (typeof File !== 'undefined' && file instanceof File) {
705
+ retVal = {
706
+ name: file.name,
707
+ mediaType: file.type,
708
+ size: file.size,
709
+ data: file
710
+ };
711
+ }
712
+ else if (typeof file === 'string' && isDataUrl(file)) {
713
+ const result = dataURItoBlob(file);
714
+ if (result !== null) {
715
+ const { blob, name } = result;
716
+ retVal = {
717
+ name: name,
718
+ mediaType: blob.type,
719
+ size: blob.size,
720
+ data: blob
721
+ };
722
+ }
723
+ }
724
+ else {
725
+ let jFile = file;
726
+ try {
727
+ jFile = JSON.parse(file);
728
+ retVal = jFile;
729
+ if (!retVal.mediaType) {
730
+ retVal.mediaType = retVal.type;
731
+ }
732
+ }
733
+ catch (ex) {
734
+ }
735
+ if (typeof jFile?.data === 'string' && isDataUrl(jFile?.data)) {
736
+ const result = dataURItoBlob(jFile?.data);
737
+ if (result !== null) {
738
+ const blob = result.blob;
739
+ retVal = {
740
+ name: jFile?.name,
741
+ mediaType: jFile?.type || jFile?.mediaType,
742
+ size: blob.size,
743
+ data: blob
744
+ };
745
+ }
746
+ }
747
+ else if (typeof jFile === 'string') {
748
+ const fileName = jFile.split('/').pop();
749
+ retVal = {
750
+ name: fileName,
751
+ mediaType: 'application/octet-stream',
752
+ size: 0,
753
+ data: jFile
754
+ };
755
+ }
756
+ else if (typeof jFile === 'object') {
757
+ retVal = {
758
+ name: jFile?.name,
759
+ mediaType: jFile?.type || jFile?.mediaType,
760
+ size: jFile?.size,
761
+ data: jFile?.data
762
+ };
763
+ }
764
+ }
765
+ if (retVal !== null && retVal.data != null) {
766
+ return new FileObject(retVal);
785
767
  }
768
+ return null;
786
769
  }
787
- get form() {
788
- return this._options.form;
770
+ else {
771
+ return null;
789
772
  }
790
- get ruleEngine() {
791
- return this.form.ruleEngine;
773
+ };
774
+ const dataURItoBlob = (dataURI) => {
775
+ const regex = /^data:([a-z]+\/[a-z0-9-+.]+)?(?:;name=([^;]+))?(;base64)?,(.+)$/;
776
+ const groups = regex.exec(dataURI);
777
+ if (groups !== null) {
778
+ const type = groups[1] || '';
779
+ const name = groups[2] || 'unknown';
780
+ const isBase64 = typeof groups[3] === 'string';
781
+ if (isBase64) {
782
+ const binary = atob(groups[4]);
783
+ const array = [];
784
+ for (let i = 0; i < binary.length; i++) {
785
+ array.push(binary.charCodeAt(i));
786
+ }
787
+ const blob = new window.Blob([new Uint8Array(array)], { type });
788
+ return { name, blob };
789
+ }
790
+ else {
791
+ const blob = new window.Blob([groups[4]], { type });
792
+ return { name, blob };
793
+ }
792
794
  }
793
- get label() {
794
- return this._jsonModel.label;
795
+ else {
796
+ return null;
795
797
  }
796
- set label(l) {
797
- if (l !== this._jsonModel.label) {
798
- const changeAction = propertyChange('label', l, this._jsonModel.label);
799
- this._jsonModel = {
800
- ...this._jsonModel,
801
- label: l
802
- };
803
- this.notifyDependents(changeAction);
804
- }
798
+ };
799
+ const isFormOrSiteContainer = (model) => {
800
+ return (':items' in model || 'cqItems' in model) && (':itemsOrder' in model || 'cqItemsOrder' in model);
801
+ };
802
+ const sitesModelToFormModel = (sitesModel) => {
803
+ if (!sitesModel || !Object.keys(sitesModel).length) {
804
+ return sitesModel;
805
805
  }
806
- get uniqueItems() {
807
- return this._jsonModel.uniqueItems;
806
+ if (isFormOrSiteContainer(sitesModel)) {
807
+ const itemsArr = [];
808
+ const itemsOrder = sitesModel[':itemsOrder'] || sitesModel.cqItemsOrder;
809
+ const items = sitesModel[':items'] || sitesModel.cqItems;
810
+ itemsOrder.forEach((elemName) => {
811
+ itemsArr.push(sitesModelToFormModel(items[elemName]));
812
+ });
813
+ sitesModel.items = itemsArr;
808
814
  }
809
- isTransparent() {
810
- const isNonTransparent = this.parent?._jsonModel.type === 'array';
811
- return !this._jsonModel.name && !isNonTransparent;
815
+ return sitesModel;
816
+ };
817
+ const replaceTemplatePlaceholders = (str, values = []) => {
818
+ return str?.replace(/\${(\d+)}/g, (match, index) => {
819
+ const replacement = values[index];
820
+ return typeof replacement !== 'undefined' ? replacement : match;
821
+ });
822
+ };
823
+
824
+ const dateRegex = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
825
+ const emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
826
+ const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
827
+ const daysInMonth = (leapYear, month) => {
828
+ if (leapYear && month == 2) {
829
+ return 29;
812
830
  }
813
- getState(forRestore = false) {
831
+ return days[month - 1];
832
+ };
833
+ const isLeapYear = (year) => {
834
+ return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0;
835
+ };
836
+ const coerceType = (param, type) => {
837
+ let num;
838
+ switch (type) {
839
+ case 'string':
840
+ return param + '';
841
+ case 'number':
842
+ num = +param;
843
+ if (!isNaN(num)) {
844
+ return num;
845
+ }
846
+ break;
847
+ case 'boolean':
848
+ if (typeof param === 'string') {
849
+ return param === 'true';
850
+ }
851
+ else if (typeof param === 'number') {
852
+ return param !== 0;
853
+ }
854
+ }
855
+ throw `${param} has invalid type. Expected : ${type}, Actual ${typeof param}`;
856
+ };
857
+ const checkNumber = (inputVal) => {
858
+ if (inputVal === '' || inputVal == null) {
814
859
  return {
815
- ...this._jsonModel,
816
- properties: this.properties,
817
- index: this.index,
818
- parent: undefined,
819
- qualifiedName: this.qualifiedName,
820
- repeatable: this.repeatable === true ? true : undefined,
821
- ':type': this[':type'],
822
- ...(forRestore ? {
823
- _dependents: this._dependents.length ? this._dependents.map(x => x.node.id) : undefined,
824
- allowedComponents: undefined,
825
- columnClassNames: undefined,
826
- columnCount: undefined,
827
- gridClassNames: undefined
828
- } : {})
860
+ value: '', valid: true
829
861
  };
830
862
  }
831
- subscribe(callback, eventName = 'change') {
832
- this._callbacks[eventName] = this._callbacks[eventName] || [];
833
- this._callbacks[eventName].push(callback);
863
+ let value = parseFloat(inputVal);
864
+ const valid = !isNaN(value);
865
+ if (!valid) {
866
+ value = inputVal;
867
+ }
868
+ return {
869
+ value, valid
870
+ };
871
+ };
872
+ const checkInteger = (inputVal) => {
873
+ if (inputVal == '' || inputVal == null) {
834
874
  return {
835
- unsubscribe: () => {
836
- this._callbacks[eventName] = this._callbacks[eventName].filter(x => x !== callback);
837
- }
875
+ value: '', valid: true
838
876
  };
839
877
  }
840
- _addDependent(dependent) {
841
- if (this._dependents.find(({ node }) => node === dependent) === undefined) {
842
- const subscription = this.subscribe((change) => {
843
- const changes = change.payload.changes;
844
- const propsToLook = [...dynamicProps, 'items'];
845
- const isPropChanged = changes.findIndex(x => {
846
- return propsToLook.indexOf(x.propertyName) > -1;
847
- }) > -1;
848
- if (isPropChanged) {
849
- dependent.dispatch(new ExecuteRule());
850
- }
851
- });
852
- this._dependents.push({ node: dependent, subscription });
853
- }
878
+ let value = parseFloat(inputVal);
879
+ const valid = !isNaN(value) && Math.round(value) === value;
880
+ if (!valid) {
881
+ value = inputVal;
854
882
  }
855
- removeDependent(dependent) {
856
- const index = this._dependents.findIndex(({ node }) => node === dependent);
857
- if (index > -1) {
858
- this._dependents[index].subscription.unsubscribe();
859
- this._dependents.splice(index, 1);
860
- }
861
- }
862
- queueEvent(action) {
863
- const actionWithTarget = new ActionImplWithTarget(action, this);
864
- this.form.getEventQueue().queue(this, actionWithTarget, ['valid', 'invalid'].indexOf(actionWithTarget.type) > -1);
865
- }
866
- dispatch(action) {
867
- this.queueEvent(action);
868
- this.form.getEventQueue().runPendingQueue();
869
- }
870
- notifyDependents(action) {
871
- const depsToRestore = this._jsonModel._dependents;
872
- if (depsToRestore) {
873
- depsToRestore.forEach((x) => {
874
- const node = this.form.getElement(x);
875
- if (node) {
876
- this._addDependent(node);
877
- }
878
- });
879
- this._jsonModel._dependents = undefined;
880
- }
881
- const handlers = this._callbacks[action.type] || [];
882
- handlers.forEach(x => {
883
- x(new ActionImplWithTarget(action, this));
884
- });
883
+ return {
884
+ value, valid
885
+ };
886
+ };
887
+ const toArray = (inputVal) => {
888
+ if (inputVal != null && !(inputVal instanceof Array)) {
889
+ return [inputVal];
885
890
  }
886
- isEmpty(value = this._jsonModel.value) {
887
- return value === undefined || value === null || value === '';
891
+ return inputVal;
892
+ };
893
+ const checkBool = (inputVal) => {
894
+ const valid = typeof inputVal === 'boolean' || inputVal === 'true' || inputVal === 'false';
895
+ const value = typeof inputVal === 'boolean' ? inputVal : (valid ? inputVal === 'true' : inputVal);
896
+ return { valid, value };
897
+ };
898
+ const checkFile = (inputVal) => {
899
+ const value = extractFileInfo(inputVal);
900
+ const valid = value !== null;
901
+ return {
902
+ value: valid ? value : inputVal,
903
+ valid
904
+ };
905
+ };
906
+ const matchMediaType = (mediaType, accepts) => {
907
+ return !mediaType || accepts.some((accept) => {
908
+ const trimmedAccept = accept.trim();
909
+ const prefixAccept = trimmedAccept.split('/')[0];
910
+ const suffixAccept = trimmedAccept.split('.')[1];
911
+ return ((trimmedAccept.includes('*') && mediaType.startsWith(prefixAccept)) ||
912
+ (trimmedAccept.includes('.') && mediaType.endsWith(suffixAccept)) ||
913
+ (trimmedAccept === mediaType));
914
+ });
915
+ };
916
+ const partitionArray = (inputVal, validatorFn) => {
917
+ const value = toArray(inputVal);
918
+ if (value == null) {
919
+ return [[], [value]];
888
920
  }
889
- _setProperty(prop, newValue, notify = true, notifyChildren = (action) => { }) {
890
- const oldValue = this._jsonModel[prop];
891
- let isValueSame = false;
892
- if (newValue !== null && oldValue !== null &&
893
- typeof newValue === 'object' && typeof oldValue === 'object') {
894
- isValueSame = JSON.stringify(newValue) === JSON.stringify(oldValue);
895
- }
896
- else {
897
- isValueSame = oldValue === newValue;
898
- }
899
- if (!isValueSame) {
900
- this._jsonModel[prop] = newValue;
901
- const changeAction = propertyChange(prop, newValue, oldValue);
902
- if (notify) {
903
- this.notifyDependents(changeAction);
904
- }
905
- notifyChildren.call(this, changeAction);
906
- return changeAction.payload.changes;
921
+ return value.reduce((acc, x) => {
922
+ if (acc[1].length == 0) {
923
+ const r = validatorFn(x);
924
+ const index = r.valid ? 0 : 1;
925
+ acc[index].push(r.value);
907
926
  }
908
- return [];
909
- }
910
- _bindToDataModel(contextualDataModel) {
911
- if (this.id === '$form') {
912
- this._data = contextualDataModel;
913
- return;
927
+ return acc;
928
+ }, [[], []]);
929
+ };
930
+ const ValidConstraints = {
931
+ date: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'format'],
932
+ string: ['minLength', 'maxLength', 'pattern'],
933
+ number: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
934
+ array: ['minItems', 'maxItems', 'uniqueItems'],
935
+ file: ['accept', 'maxFileSize'],
936
+ email: ['minLength', 'maxLength', 'format', 'pattern']
937
+ };
938
+ const validationConstraintsList = ['type', 'format', 'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'minItems',
939
+ 'maxItems', 'uniqueItems', 'minLength', 'maxLength', 'pattern', 'required', 'enum', 'accept', 'maxFileSize'];
940
+ const Constraints = {
941
+ type: (constraint, inputVal) => {
942
+ let value = inputVal;
943
+ if (inputVal == undefined) {
944
+ return {
945
+ valid: true,
946
+ value: inputVal
947
+ };
914
948
  }
915
- const dataRef = this._jsonModel.dataRef;
916
- let _data, _parent = contextualDataModel, _key = '';
917
- if (dataRef === null) {
918
- _data = NullDataValue;
949
+ let valid = true, res;
950
+ switch (constraint) {
951
+ case 'string':
952
+ valid = true;
953
+ value = inputVal.toString();
954
+ break;
955
+ case 'string[]':
956
+ value = toArray(inputVal);
957
+ break;
958
+ case 'number':
959
+ res = checkNumber(inputVal);
960
+ value = res.value;
961
+ valid = res.valid;
962
+ break;
963
+ case 'boolean':
964
+ res = checkBool(inputVal);
965
+ valid = res.valid;
966
+ value = res.value;
967
+ break;
968
+ case 'integer':
969
+ res = checkInteger(inputVal);
970
+ valid = res.valid;
971
+ value = res.value;
972
+ break;
973
+ case 'integer[]':
974
+ res = partitionArray(inputVal, checkInteger);
975
+ valid = res[1].length === 0;
976
+ value = valid ? res[0] : inputVal;
977
+ break;
978
+ case 'file':
979
+ res = checkFile(inputVal instanceof Array ? inputVal[0] : inputVal);
980
+ valid = res.valid;
981
+ value = res.value;
982
+ break;
983
+ case 'file[]':
984
+ res = partitionArray(inputVal, checkFile);
985
+ valid = res[1].length === 0;
986
+ value = valid ? res[0] : inputVal;
987
+ break;
988
+ case 'number[]':
989
+ res = partitionArray(inputVal, checkNumber);
990
+ valid = res[1].length === 0;
991
+ value = valid ? res[0] : inputVal;
992
+ break;
993
+ case 'boolean[]':
994
+ res = partitionArray(inputVal, checkBool);
995
+ valid = res[1].length === 0;
996
+ value = valid ? res[0] : inputVal;
997
+ break;
919
998
  }
920
- else if (dataRef !== undefined && !this.repeatable) {
921
- if (this._tokens.length === 0) {
922
- this._tokens = tokenize(dataRef);
923
- }
924
- let searchData = contextualDataModel;
925
- if (this._tokens[0].type === TOK_GLOBAL) {
926
- searchData = this.form.getDataNode();
927
- }
928
- else if (this._tokens[0].type === TOK_REPEATABLE) {
929
- let repeatRoot = this.parent;
930
- while (!repeatRoot.repeatable && repeatRoot !== this.form) {
931
- repeatRoot = repeatRoot.parent;
932
- }
933
- searchData = repeatRoot.getDataNode();
934
- }
935
- if (typeof searchData !== 'undefined') {
936
- const name = this._tokens[this._tokens.length - 1].value;
937
- const create = this.defaultDataModel(name);
938
- _data = resolveData(searchData, this._tokens, create);
939
- _parent = resolveData(searchData, this._tokens.slice(0, -1));
940
- _key = name;
941
- }
999
+ return {
1000
+ valid,
1001
+ value
1002
+ };
1003
+ },
1004
+ format: (constraint, input) => {
1005
+ let valid = true;
1006
+ const value = input;
1007
+ if (input === null) {
1008
+ return { value, valid };
942
1009
  }
943
- else {
944
- if (contextualDataModel !== NullDataValue && staticFields.indexOf(this.fieldType) === -1) {
945
- _parent = contextualDataModel;
946
- const name = this._jsonModel.name || '';
947
- const key = contextualDataModel.$type === 'array' ? this.index : name;
948
- _key = key;
949
- if (key !== '') {
950
- const create = this.defaultDataModel(key);
951
- if (create !== undefined) {
952
- _data = contextualDataModel.$getDataNode(key);
953
- if (_data === undefined) {
954
- _data = create;
955
- contextualDataModel.$addDataNode(key, _data);
956
- }
957
- }
1010
+ let res;
1011
+ switch (constraint) {
1012
+ case 'date':
1013
+ res = dateRegex.exec((input || '').trim());
1014
+ if (res != null) {
1015
+ const [match, year, month, date] = res;
1016
+ const [nMonth, nDate] = [+month, +date];
1017
+ const leapYear = isLeapYear(+year);
1018
+ valid = (nMonth >= 1 && nMonth <= 12) &&
1019
+ (nDate >= 1 && nDate <= daysInMonth(leapYear, nMonth));
958
1020
  }
959
1021
  else {
960
- _data = undefined;
1022
+ valid = false;
961
1023
  }
962
- }
1024
+ break;
1025
+ case 'email':
1026
+ valid = new RegExp(emailRegex).test((input || '').trim());
1027
+ break;
1028
+ case 'data-url':
1029
+ valid = true;
1030
+ break;
963
1031
  }
964
- if (_data) {
965
- if (!this.isContainer && _parent !== NullDataValue && _data !== NullDataValue) {
966
- _data = _data?.$convertToDataValue();
967
- _parent.$addDataNode(_key, _data, true);
968
- }
969
- _data?.$bindToField(this);
970
- this._data = _data;
1032
+ return { valid, value };
1033
+ },
1034
+ minimum: (constraint, value) => {
1035
+ return { valid: value >= constraint, value };
1036
+ },
1037
+ maximum: (constraint, value) => {
1038
+ return { valid: value <= constraint, value };
1039
+ },
1040
+ exclusiveMinimum: (constraint, value) => {
1041
+ return { valid: value > constraint, value };
1042
+ },
1043
+ exclusiveMaximum: (constraint, value) => {
1044
+ return { valid: value < constraint, value };
1045
+ },
1046
+ minItems: (constraint, value) => {
1047
+ return { valid: (value instanceof Array) && value.length >= constraint, value };
1048
+ },
1049
+ maxItems: (constraint, value) => {
1050
+ return { valid: (value instanceof Array) && value.length <= constraint, value };
1051
+ },
1052
+ uniqueItems: (constraint, value) => {
1053
+ return { valid: !constraint || ((value instanceof Array) && value.length === new Set(value).size), value };
1054
+ },
1055
+ minLength: (constraint, value) => {
1056
+ return { ...Constraints.minimum(constraint, typeof value === 'string' ? value.length : 0), value };
1057
+ },
1058
+ maxLength: (constraint, value) => {
1059
+ return { ...Constraints.maximum(constraint, typeof value === 'string' ? value.length : 0), value };
1060
+ },
1061
+ pattern: (constraint, value) => {
1062
+ let regex;
1063
+ if (typeof constraint === 'string') {
1064
+ regex = new RegExp(constraint);
971
1065
  }
972
- }
973
- _data;
974
- getDataNode() {
975
- return this._data;
976
- }
977
- get lang() {
978
- if (this._jsonModel.lang) {
979
- this._lang = this._jsonModel.lang;
1066
+ else {
1067
+ regex = constraint;
980
1068
  }
981
- if (!this._lang) {
982
- if (this.parent) {
983
- this._lang = this.parent.lang;
984
- }
985
- else {
986
- this._lang = Intl.DateTimeFormat().resolvedOptions().locale;
987
- }
1069
+ return { valid: regex.test(value), value };
1070
+ },
1071
+ required: (constraint, value) => {
1072
+ const valid = constraint ? value != null && value !== '' : true;
1073
+ return { valid, value };
1074
+ },
1075
+ enum: (constraint, value) => {
1076
+ return {
1077
+ valid: constraint.indexOf(value) > -1,
1078
+ value
1079
+ };
1080
+ },
1081
+ accept: (constraint, value) => {
1082
+ if (!constraint || constraint.length === 0 || value === null || value === undefined) {
1083
+ return {
1084
+ valid: true,
1085
+ value
1086
+ };
988
1087
  }
989
- return this._lang;
990
- }
991
- get properties() {
992
- return this._jsonModel.properties || {};
1088
+ const tempValue = value instanceof Array ? value : [value];
1089
+ const invalidFile = tempValue.some((file) => !matchMediaType(file.type, constraint));
1090
+ return {
1091
+ valid: !invalidFile,
1092
+ value
1093
+ };
1094
+ },
1095
+ maxFileSize: (constraint, value) => {
1096
+ const sizeLimit = typeof constraint === 'string' ? getFileSizeInBytes(constraint) : constraint;
1097
+ return {
1098
+ valid: !(value instanceof FileObject) || value.size <= sizeLimit,
1099
+ value
1100
+ };
993
1101
  }
994
- set properties(p) {
995
- this._setProperty('properties', { ...p });
1102
+ };
1103
+
1104
+ const editableProperties = [
1105
+ 'value',
1106
+ 'label',
1107
+ 'description',
1108
+ 'visible',
1109
+ 'enabled',
1110
+ 'valid',
1111
+ 'errorMessage',
1112
+ 'readOnly',
1113
+ 'enum',
1114
+ 'enumNames',
1115
+ 'required',
1116
+ 'properties',
1117
+ 'exclusiveMinimum',
1118
+ 'exclusiveMaximum',
1119
+ 'maximum',
1120
+ 'maxItems',
1121
+ 'minimum',
1122
+ 'minItems',
1123
+ 'checked'
1124
+ ];
1125
+ const dynamicProps = [
1126
+ ...editableProperties,
1127
+ 'index',
1128
+ 'activeChild'
1129
+ ];
1130
+ const staticFields = ['plain-text', 'image'];
1131
+ class ActionImplWithTarget {
1132
+ _action;
1133
+ _target;
1134
+ constructor(_action, _target) {
1135
+ this._action = _action;
1136
+ this._target = _target;
996
1137
  }
997
- getNonTransparentParent() {
998
- let nonTransparentParent = this.parent;
999
- while (nonTransparentParent != null && nonTransparentParent.isTransparent()) {
1000
- nonTransparentParent = nonTransparentParent.parent;
1001
- }
1002
- return nonTransparentParent;
1138
+ get type() {
1139
+ return this._action.type;
1003
1140
  }
1004
- _initialize(mode) {
1005
- if (typeof this._data === 'undefined') {
1006
- let dataNode, parent = this.parent;
1007
- do {
1008
- dataNode = parent.getDataNode();
1009
- parent = parent.parent;
1010
- } while (dataNode === undefined);
1011
- this._bindToDataModel(dataNode);
1012
- }
1141
+ get payload() {
1142
+ return this._action.payload;
1013
1143
  }
1014
- _applyUpdates(propNames, updates) {
1015
- return propNames.reduce((acc, propertyName) => {
1016
- const currentValue = updates[propertyName];
1017
- const changes = this._setProperty(propertyName, currentValue, false);
1018
- if (changes.length > 0) {
1019
- acc[propertyName] = changes[0];
1020
- }
1021
- return acc;
1022
- }, {});
1144
+ get metadata() {
1145
+ return this._action.metadata;
1023
1146
  }
1024
- get qualifiedName() {
1025
- if (this.isTransparent()) {
1026
- return null;
1027
- }
1028
- if (this[qualifiedName] !== null) {
1029
- return this[qualifiedName];
1030
- }
1031
- const parent = this.getNonTransparentParent();
1032
- if (parent && parent.type === 'array') {
1033
- this[qualifiedName] = `${parent.qualifiedName}[${this.index}]`;
1034
- }
1035
- else {
1036
- this[qualifiedName] = `${parent.qualifiedName}.${this.name}`;
1037
- }
1038
- return this[qualifiedName];
1147
+ get target() {
1148
+ return this._target;
1039
1149
  }
1040
- focus() {
1041
- if (this.parent) {
1042
- this.parent.activeChild = this;
1043
- }
1150
+ get isCustomEvent() {
1151
+ return this._action.isCustomEvent;
1044
1152
  }
1045
- _getDefaults() {
1046
- return {};
1153
+ get originalAction() {
1154
+ return this._action.originalAction;
1047
1155
  }
1048
- _applyDefaultsInModel() {
1049
- Object.entries(this._getDefaults()).map(([key, value]) => {
1050
- if (this._jsonModel[key] === undefined && value !== undefined) {
1051
- this._jsonModel[key] = value;
1052
- }
1053
- else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1054
- Object.keys(value).forEach((keyOfValue) => {
1055
- if (this._jsonModel[key][keyOfValue] === undefined) {
1056
- this._jsonModel[key][keyOfValue] = value[keyOfValue];
1057
- }
1058
- });
1059
- }
1060
- });
1156
+ toString() {
1157
+ return this._action.toString();
1061
1158
  }
1062
1159
  }
1063
- __decorate([
1064
- dependencyTracked()
1065
- ], BaseNode.prototype, "index", null);
1066
- __decorate([
1067
- dependencyTracked()
1068
- ], BaseNode.prototype, "description", null);
1069
- __decorate([
1070
- dependencyTracked()
1071
- ], BaseNode.prototype, "visible", null);
1072
- __decorate([
1073
- dependencyTracked()
1074
- ], BaseNode.prototype, "label", null);
1075
- __decorate([
1076
- dependencyTracked()
1077
- ], BaseNode.prototype, "properties", null);
1078
-
1079
- class Scriptable extends BaseNode {
1080
- _events = {};
1081
- _rules = {};
1082
- getRules() {
1083
- return typeof this._jsonModel.rules !== 'object' ? {} : this._jsonModel.rules;
1084
- }
1085
- getCompiledRule(eName, rule) {
1086
- if (!(eName in this._rules)) {
1087
- const eString = rule || this.getRules()[eName];
1088
- if (typeof eString === 'string' && eString.length > 0) {
1089
- try {
1090
- this._rules[eName] = this.ruleEngine.compileRule(eString, this.lang);
1091
- }
1092
- catch (e) {
1093
- this.form.logger.error(`Unable to compile rule \`"${eName}" : "${eString}"\` Exception : ${e}`);
1094
- }
1095
- }
1096
- else {
1097
- throw new Error(`only expression strings are supported. ${typeof (eString)} types are not supported`);
1098
- }
1160
+ const target = Symbol('target');
1161
+ const qualifiedName = Symbol('qualifiedName');
1162
+ function dependencyTracked() {
1163
+ return function (target, propertyKey, descriptor) {
1164
+ const get = descriptor.get;
1165
+ if (get != undefined) {
1166
+ descriptor.get = function () {
1167
+ this.ruleEngine.trackDependency(this);
1168
+ return get.call(this);
1169
+ };
1099
1170
  }
1100
- return this._rules[eName];
1101
- }
1102
- getCompiledEvent(eName) {
1103
- if (!(eName in this._events)) {
1104
- let eString = this._jsonModel.events?.[eName];
1105
- if (typeof eString === 'string' && eString.length > 0) {
1106
- eString = [eString];
1171
+ };
1172
+ }
1173
+ const addOnly = (includeOrExclude) => (...fieldTypes) => (target, propertyKey, descriptor) => {
1174
+ const get = descriptor.get;
1175
+ if (get != undefined) {
1176
+ descriptor.get = function () {
1177
+ if (fieldTypes.indexOf(this.fieldType) > -1 === includeOrExclude) {
1178
+ return get.call(this);
1107
1179
  }
1108
- if (typeof eString !== 'undefined' && eString.length > 0) {
1109
- this._events[eName] = eString.map(x => {
1110
- try {
1111
- return this.ruleEngine.compileRule(x, this.lang);
1112
- }
1113
- catch (e) {
1114
- this.form.logger.error(`Unable to compile expression \`"${eName}" : "${eString}"\` Exception : ${e}`);
1115
- }
1116
- return null;
1117
- }).filter(x => x !== null);
1180
+ return undefined;
1181
+ };
1182
+ }
1183
+ const set = descriptor.set;
1184
+ if (set != undefined) {
1185
+ descriptor.set = function (value) {
1186
+ if (fieldTypes.indexOf(this.fieldType) > -1 === includeOrExclude) {
1187
+ set.call(this, value);
1118
1188
  }
1119
- }
1120
- return this._events[eName] || [];
1189
+ };
1190
+ }
1191
+ };
1192
+ const include = addOnly(true);
1193
+ const exclude = addOnly(false);
1194
+ class BaseNode {
1195
+ _options;
1196
+ _ruleNode;
1197
+ _lang = '';
1198
+ _callbacks = {};
1199
+ _dependents = [];
1200
+ _jsonModel;
1201
+ _tokens = [];
1202
+ get isContainer() {
1203
+ return false;
1121
1204
  }
1122
- applyUpdates(updates) {
1123
- Object.entries(updates).forEach(([key, value]) => {
1124
- if (key in editableProperties || (key in this && typeof this[key] !== 'function')) {
1125
- try {
1126
- this[key] = value;
1127
- }
1128
- catch (e) {
1129
- console.error(e);
1130
- }
1205
+ constructor(params, _options) {
1206
+ this._options = _options;
1207
+ this[qualifiedName] = null;
1208
+ this._jsonModel = {
1209
+ ...params,
1210
+ id: 'id' in params ? params.id : this.form.getUniqueId()
1211
+ };
1212
+ }
1213
+ setupRuleNode() {
1214
+ const self = this;
1215
+ this._ruleNode = new Proxy(this.ruleNodeReference(), {
1216
+ get: (ruleNodeReference, prop) => {
1217
+ return self.getFromRule(ruleNodeReference, prop);
1131
1218
  }
1132
1219
  });
1133
1220
  }
1134
- executeAllRules(context) {
1135
- const entries = Object.entries(this.getRules());
1136
- if (entries.length > 0) {
1137
- const scope = this.getExpressionScope();
1138
- entries.forEach(([prop, rule]) => {
1139
- const node = this.getCompiledRule(prop, rule);
1140
- if (node) {
1141
- const newVal = this.ruleEngine.execute(node, scope, context, true);
1142
- if (editableProperties.indexOf(prop) > -1) {
1143
- const oldAndNewValueAreEmpty = this.isEmpty() && this.isEmpty(newVal) && prop === 'value';
1144
- if (!oldAndNewValueAreEmpty) {
1145
- this[prop] = newVal;
1146
- }
1147
- }
1148
- else {
1149
- this.form.logger.warn(`${prop} is not a valid editable property.`);
1150
- }
1151
- }
1152
- });
1153
- }
1221
+ ruleNodeReference() {
1222
+ return this;
1154
1223
  }
1155
- getExpressionScope() {
1156
- const parent = this.getNonTransparentParent();
1157
- const target = {
1158
- self: this.getRuleNode(),
1159
- siblings: parent?.ruleNodeReference() || {}
1160
- };
1161
- const scope = new Proxy(target, {
1162
- get: (target, prop) => {
1163
- if (prop === Symbol.toStringTag) {
1164
- return 'Object';
1165
- }
1166
- if (typeof prop === 'string' && prop.startsWith('$')) {
1167
- const retValue = target.self[prop];
1224
+ getRuleNode() {
1225
+ return this._ruleNode;
1226
+ }
1227
+ getFromRule(ruleNodeReference, prop) {
1228
+ if (prop === Symbol.toPrimitive || (prop === 'valueOf' && !ruleNodeReference.hasOwnProperty('valueOf'))) {
1229
+ return this.valueOf;
1230
+ }
1231
+ else if (prop === target) {
1232
+ return this;
1233
+ }
1234
+ else if (typeof (prop) === 'string') {
1235
+ if (prop.startsWith('$')) {
1236
+ prop = prop.substr(1);
1237
+ if (typeof this[prop] !== 'function') {
1238
+ const retValue = this[prop];
1168
1239
  if (retValue instanceof BaseNode) {
1169
1240
  return retValue.getRuleNode();
1170
1241
  }
@@ -1175,918 +1246,1135 @@ class Scriptable extends BaseNode {
1175
1246
  return retValue;
1176
1247
  }
1177
1248
  }
1178
- else {
1179
- if (prop in target.siblings) {
1180
- return target.siblings[prop];
1181
- }
1182
- else {
1183
- return target.self[prop];
1184
- }
1249
+ }
1250
+ else {
1251
+ if (ruleNodeReference.hasOwnProperty(prop)) {
1252
+ return ruleNodeReference[prop];
1253
+ }
1254
+ else if (typeof ruleNodeReference[prop] === 'function') {
1255
+ return ruleNodeReference[prop];
1185
1256
  }
1186
- },
1187
- has: (target, prop) => {
1188
- prop = prop;
1189
- const selfPropertyOrChild = target.self[prop];
1190
- const sibling = target.siblings[prop];
1191
- return typeof selfPropertyOrChild != 'undefined' || typeof sibling != 'undefined';
1192
1257
  }
1193
- });
1194
- return scope;
1195
- }
1196
- executeEvent(context, node) {
1197
- let updates;
1198
- if (node) {
1199
- updates = this.ruleEngine.execute(node, this.getExpressionScope(), context);
1200
- }
1201
- if (typeof updates !== 'undefined' && updates != null) {
1202
- this.applyUpdates(updates);
1203
- }
1204
- }
1205
- executeRule(event, context) {
1206
- if (typeof event.payload.ruleName === 'undefined') {
1207
- this.executeAllRules(context);
1208
1258
  }
1209
1259
  }
1210
- executeExpression(expr) {
1211
- const ruleContext = {
1212
- 'form': this.form,
1213
- '$form': this.form.getRuleNode(),
1214
- '$field': this.getRuleNode(),
1215
- 'field': this
1216
- };
1217
- const node = this.ruleEngine.compileRule(expr, this.lang);
1218
- return this.ruleEngine.execute(node, this.getExpressionScope(), ruleContext);
1260
+ get id() {
1261
+ return this._jsonModel.id;
1219
1262
  }
1220
- executeAction(action) {
1221
- const context = {
1222
- 'form': this.form,
1223
- '$form': this.form.getRuleNode(),
1224
- '$field': this.getRuleNode(),
1225
- 'field': this,
1226
- '$event': {
1227
- type: action.type,
1228
- payload: action.payload,
1229
- target: this.getRuleNode()
1230
- }
1231
- };
1232
- const eventName = action.isCustomEvent ? `custom:${action.type}` : action.type;
1233
- const funcName = action.isCustomEvent ? `custom_${action.type}` : action.type;
1234
- const node = this.getCompiledEvent(eventName);
1235
- if (funcName in this && typeof this[funcName] === 'function') {
1236
- this[funcName](action, context);
1263
+ get index() {
1264
+ if (this.parent) {
1265
+ return this.parent.indexOf(this);
1237
1266
  }
1238
- node.forEach((n) => this.executeEvent(context, n));
1239
- this.notifyDependents(action);
1240
- }
1241
- }
1242
-
1243
- const notifyChildrenAttributes = [
1244
- 'readOnly', 'enabled'
1245
- ];
1246
- class Container extends Scriptable {
1247
- _children = [];
1248
- _childrenReference;
1249
- _itemTemplate = null;
1250
- fieldFactory;
1251
- constructor(json, _options) {
1252
- super(json, { form: _options.form, parent: _options.parent, mode: _options.mode });
1253
- this.fieldFactory = _options.fieldFactory;
1254
- }
1255
- _getDefaults() {
1256
- return {
1257
- ...super._getDefaults(),
1258
- enabled: true,
1259
- readOnly: false
1260
- };
1261
- }
1262
- ruleNodeReference() {
1263
- return this._childrenReference;
1264
- }
1265
- get items() {
1266
- return this._children;
1267
+ return 0;
1267
1268
  }
1268
- get maxItems() {
1269
- return this._jsonModel.maxItems;
1269
+ get parent() {
1270
+ return this._options.parent;
1270
1271
  }
1271
- set maxItems(m) {
1272
- this._jsonModel.maxItems = m;
1273
- const minItems = this._jsonModel.minItems || 1;
1274
- const itemsLength = this._children.length;
1275
- const items2Remove = Math.min(itemsLength - m, itemsLength - minItems);
1276
- if (items2Remove > 0) {
1277
- for (let i = 0; i < items2Remove; i++) {
1278
- this.getDataNode().$removeDataNode(m + i);
1279
- this._childrenReference.pop();
1280
- }
1281
- const elems = this._children.splice(m, items2Remove);
1282
- this.notifyDependents(propertyChange('items', elems, null));
1283
- }
1272
+ get type() {
1273
+ return this._jsonModel.type;
1284
1274
  }
1285
- get minItems() {
1286
- return this._jsonModel.minItems;
1275
+ get repeatable() {
1276
+ return this.parent?.hasDynamicItems();
1287
1277
  }
1288
- set minItems(m) {
1289
- this._jsonModel.minItems = m;
1290
- const itemsLength = this._children.length;
1291
- const difference = itemsLength - m;
1292
- const items2Add = Math.abs(difference);
1293
- if (difference < 0) {
1294
- const elems = [];
1295
- for (let i = 0; i < items2Add; i++) {
1296
- elems.push(this._addChild(this._itemTemplate, null, true));
1297
- }
1298
- this.notifyDependents(propertyChange('items', elems, null));
1299
- }
1278
+ get fieldType() {
1279
+ return this._jsonModel.fieldType || 'text-input';
1300
1280
  }
1301
- hasDynamicItems() {
1302
- return this._itemTemplate != null;
1281
+ get ':type'() {
1282
+ return this._jsonModel[':type'] || this.fieldType;
1303
1283
  }
1304
- get isContainer() {
1305
- return true;
1284
+ get name() {
1285
+ return this._jsonModel.name;
1306
1286
  }
1307
- _activeChild = null;
1308
- isSiteContainer(item) {
1309
- return (':items' in item || 'cqItems' in item) && !('fieldType' in item);
1287
+ get description() {
1288
+ return this._jsonModel.description;
1310
1289
  }
1311
- isAFormField(item) {
1312
- return ('fieldType' in item || 'id' in item || 'name' in item || 'dataRef' in item || 'type' in item);
1290
+ set description(d) {
1291
+ this._setProperty('description', d);
1313
1292
  }
1314
- _getFormAndSitesState(isRepeatableChild = false, forRestore = false) {
1315
- return this._jsonModel.items ? this._jsonModel.items.map((x) => {
1316
- if (this.isSiteContainer(x)) {
1317
- const newObjWithId = {
1318
- ...(x?.id ? { id: this.form.getUniqueId() } : {})
1319
- };
1320
- return {
1321
- ...x,
1322
- ...newObjWithId,
1323
- ':items': this.walkSiteContainerItems(x)
1324
- };
1325
- }
1326
- else if (this.isAFormField(x)) {
1327
- return { ...this.form.getElement(x?.id).getState(isRepeatableChild, forRestore) };
1328
- }
1329
- else {
1330
- return x;
1331
- }
1332
- }) : [];
1293
+ get dataRef() {
1294
+ return this._jsonModel.dataRef;
1333
1295
  }
1334
- getItemsState(isRepeatableChild = false, forRestore = false) {
1335
- if (this._jsonModel.type === 'array' || isRepeatable$1(this._jsonModel) || isRepeatableChild) {
1336
- if (isRepeatableChild) {
1337
- return this._getFormAndSitesState(isRepeatableChild, forRestore);
1338
- }
1339
- else {
1340
- return this._children.map(x => {
1341
- return { ...x.getState(true, forRestore) };
1342
- });
1343
- }
1296
+ get visible() {
1297
+ if (this.parent?.visible !== undefined) {
1298
+ return this.parent?.visible ? this._jsonModel.visible : false;
1344
1299
  }
1345
1300
  else {
1346
- return this._getFormAndSitesState(isRepeatableChild, forRestore);
1301
+ return this._jsonModel.visible;
1347
1302
  }
1348
1303
  }
1349
- getState(isRepeatableChild = false, forRestore = false) {
1304
+ set visible(v) {
1305
+ if (v !== this._jsonModel.visible) {
1306
+ const changeAction = propertyChange('visible', v, this._jsonModel.visible);
1307
+ this._jsonModel.visible = v;
1308
+ this.notifyDependents(changeAction);
1309
+ }
1310
+ }
1311
+ get form() {
1312
+ return this._options.form;
1313
+ }
1314
+ get ruleEngine() {
1315
+ return this.form.ruleEngine;
1316
+ }
1317
+ get label() {
1318
+ return this._jsonModel.label;
1319
+ }
1320
+ set label(l) {
1321
+ if (l !== this._jsonModel.label) {
1322
+ const changeAction = propertyChange('label', l, this._jsonModel.label);
1323
+ this._jsonModel = {
1324
+ ...this._jsonModel,
1325
+ label: l
1326
+ };
1327
+ this.notifyDependents(changeAction);
1328
+ }
1329
+ }
1330
+ get uniqueItems() {
1331
+ return this._jsonModel.uniqueItems;
1332
+ }
1333
+ isTransparent() {
1334
+ const isNonTransparent = this.parent?._jsonModel.type === 'array';
1335
+ return !this._jsonModel.name && !isNonTransparent;
1336
+ }
1337
+ getState(forRestore = false) {
1350
1338
  return {
1351
- ...super.getState(forRestore),
1352
- ...(forRestore ? {
1353
- ':items': undefined,
1354
- ':itemsOrder': undefined
1339
+ ...this._jsonModel,
1340
+ properties: this.properties,
1341
+ index: this.index,
1342
+ parent: undefined,
1343
+ qualifiedName: this.qualifiedName,
1344
+ ...(this.repeatable === true ? {
1345
+ repeatable: true,
1346
+ minOccur: this.parent.minItems,
1347
+ maxOccur: this.parent.maxItems
1355
1348
  } : {}),
1356
- items: this.getItemsState(isRepeatableChild, forRestore),
1357
- enabled: this.enabled,
1358
- readOnly: this.readOnly
1349
+ ':type': this[':type'],
1350
+ ...(forRestore ? {
1351
+ _dependents: this._dependents.length ? this._dependents.map(x => x.node.id) : undefined,
1352
+ allowedComponents: undefined,
1353
+ columnClassNames: undefined,
1354
+ columnCount: undefined,
1355
+ gridClassNames: undefined
1356
+ } : {})
1359
1357
  };
1360
1358
  }
1361
- _createChild(child, options) {
1362
- return this.fieldFactory.createField(child, options);
1363
- }
1364
- walkSiteContainerItems(x) {
1365
- return Object.fromEntries(Object.entries(x[':items']).map(([key, value]) => {
1366
- if (this.isAFormField(value)) {
1367
- return [key, this.form.getElement(value?.id).getState()];
1368
- }
1369
- else if (this.isSiteContainer(value)) {
1370
- return this.walkSiteContainerItems(value);
1371
- }
1372
- else {
1373
- if (typeof value === 'object') {
1374
- const newObjWithId = {
1375
- ...(value?.id ? { id: this.form.getUniqueId() } : {})
1376
- };
1377
- return [key, {
1378
- ...value,
1379
- ...newObjWithId
1380
- }
1381
- ];
1382
- }
1383
- else {
1384
- return [key, value];
1385
- }
1359
+ subscribe(callback, eventName = 'change') {
1360
+ this._callbacks[eventName] = this._callbacks[eventName] || [];
1361
+ this._callbacks[eventName].push(callback);
1362
+ return {
1363
+ unsubscribe: () => {
1364
+ this._callbacks[eventName] = this._callbacks[eventName].filter(x => x !== callback);
1386
1365
  }
1387
- }));
1366
+ };
1388
1367
  }
1389
- _addChildToRuleNode(child, options) {
1390
- const self = this;
1391
- const { parent = this } = options;
1392
- const name = parent.type == 'array' ? parent._children.length + '' : child.name || '';
1393
- if (name.length > 0) {
1394
- Object.defineProperty(parent._childrenReference, name, {
1395
- get: () => {
1396
- if (child.isContainer && child.hasDynamicItems()) {
1397
- self.ruleEngine.trackDependency(child);
1398
- }
1399
- if (self.hasDynamicItems()) {
1400
- self.ruleEngine.trackDependency(self);
1401
- if (this._children[name] !== undefined) {
1402
- return this._children[name].getRuleNode();
1403
- }
1404
- }
1405
- else {
1406
- return child.getRuleNode();
1407
- }
1408
- },
1409
- configurable: true,
1410
- enumerable: true
1368
+ _addDependent(dependent) {
1369
+ if (this._dependents.find(({ node }) => node === dependent) === undefined) {
1370
+ const subscription = this.subscribe((change) => {
1371
+ const changes = change.payload.changes;
1372
+ const propsToLook = [...dynamicProps, 'items'];
1373
+ const isPropChanged = changes.findIndex(x => {
1374
+ return propsToLook.indexOf(x.propertyName) > -1;
1375
+ }) > -1;
1376
+ if (isPropChanged) {
1377
+ dependent.dispatch(new ExecuteRule());
1378
+ }
1411
1379
  });
1380
+ this._dependents.push({ node: dependent, subscription });
1412
1381
  }
1413
1382
  }
1414
- _addChild(itemJson, index, cloneIds = false, mode = 'create') {
1415
- let nonTransparentParent = this;
1416
- while (nonTransparentParent != null && nonTransparentParent.isTransparent()) {
1417
- nonTransparentParent = nonTransparentParent.parent;
1418
- }
1419
- if (typeof index !== 'number' || index > nonTransparentParent._children.length) {
1420
- index = this._children.length;
1421
- }
1422
- const form = this.form;
1423
- const itemTemplate = {
1424
- index,
1425
- ...deepClone(itemJson, cloneIds ? () => { return form.getUniqueId(); } : undefined)
1426
- };
1427
- const retVal = this._createChild(itemTemplate, { parent: this, form: this.form, mode });
1428
- itemJson.id = retVal.id;
1429
- this.form.fieldAdded(retVal);
1430
- this._addChildToRuleNode(retVal, { parent: nonTransparentParent });
1431
- if (index === this._children.length) {
1432
- this._children.push(retVal);
1383
+ removeDependent(dependent) {
1384
+ const index = this._dependents.findIndex(({ node }) => node === dependent);
1385
+ if (index > -1) {
1386
+ this._dependents[index].subscription.unsubscribe();
1387
+ this._dependents.splice(index, 1);
1433
1388
  }
1434
- else {
1435
- this._children.splice(index, 0, retVal);
1389
+ }
1390
+ queueEvent(action) {
1391
+ const actionWithTarget = new ActionImplWithTarget(action, this);
1392
+ this.form.getEventQueue().queue(this, actionWithTarget, ['valid', 'invalid'].indexOf(actionWithTarget.type) > -1);
1393
+ }
1394
+ dispatch(action) {
1395
+ this.queueEvent(action);
1396
+ this.form.getEventQueue().runPendingQueue();
1397
+ }
1398
+ notifyDependents(action) {
1399
+ const depsToRestore = this._jsonModel._dependents;
1400
+ if (depsToRestore) {
1401
+ depsToRestore.forEach((x) => {
1402
+ const node = this.form.getElement(x);
1403
+ if (node) {
1404
+ this._addDependent(node);
1405
+ }
1406
+ });
1407
+ this._jsonModel._dependents = undefined;
1436
1408
  }
1437
- return retVal;
1409
+ const handlers = this._callbacks[action.type] || [];
1410
+ handlers.forEach(x => {
1411
+ x(new ActionImplWithTarget(action, this));
1412
+ });
1438
1413
  }
1439
- indexOf(f) {
1440
- return this._children.indexOf(f);
1414
+ isEmpty(value = this._jsonModel.value) {
1415
+ return value === undefined || value === null || value === '';
1441
1416
  }
1442
- defaultDataModel(name) {
1443
- const type = this._jsonModel.type || undefined;
1444
- if (type === undefined) {
1445
- return undefined;
1417
+ _setProperty(prop, newValue, notify = true, notifyChildren = (action) => { }) {
1418
+ const oldValue = this._jsonModel[prop];
1419
+ let isValueSame = false;
1420
+ if (newValue !== null && oldValue !== null &&
1421
+ typeof newValue === 'object' && typeof oldValue === 'object') {
1422
+ isValueSame = JSON.stringify(newValue) === JSON.stringify(oldValue);
1446
1423
  }
1447
1424
  else {
1448
- const instance = type === 'array' ? [] : {};
1449
- return new DataGroup(name, instance, type);
1425
+ isValueSame = oldValue === newValue;
1450
1426
  }
1451
- }
1452
- _canHaveRepeatingChildren(mode = 'create') {
1453
- const items = this._jsonModel.items;
1454
- return this._jsonModel.type == 'array' && this.getDataNode() != null &&
1455
- (items.length === 1 || (items[0].repeatable == true && mode === 'restore'));
1456
- }
1457
- _initialize(mode) {
1458
- super._initialize(mode);
1459
- const items = this._jsonModel.items || [];
1460
- this._childrenReference = this._jsonModel.type == 'array' ? [] : {};
1461
- if (this._canHaveRepeatingChildren(mode)) {
1462
- this._itemTemplate = deepClone(items[0]);
1463
- if (mode === 'restore') {
1464
- this._itemTemplate.repeatable = undefined;
1465
- }
1466
- if (typeof (this._jsonModel.minItems) !== 'number') {
1467
- this._jsonModel.minItems = 0;
1427
+ if (!isValueSame) {
1428
+ this._jsonModel[prop] = newValue;
1429
+ const changeAction = propertyChange(prop, newValue, oldValue);
1430
+ if (notify) {
1431
+ this.notifyDependents(changeAction);
1468
1432
  }
1469
- if (typeof (this._jsonModel.maxItems) !== 'number') {
1470
- this._jsonModel.maxItems = -1;
1433
+ notifyChildren.call(this, changeAction);
1434
+ if (validationConstraintsList.includes(prop)) {
1435
+ this.validate();
1471
1436
  }
1472
- if (typeof (this._jsonModel.initialItems) !== 'number') {
1473
- this._jsonModel.initialItems = Math.max(1, this._jsonModel.minItems);
1437
+ return changeAction.payload.changes;
1438
+ }
1439
+ return [];
1440
+ }
1441
+ _bindToDataModel(contextualDataModel) {
1442
+ if (this.id === '$form') {
1443
+ this._data = contextualDataModel;
1444
+ return;
1445
+ }
1446
+ const dataRef = this._jsonModel.dataRef;
1447
+ let _data, _parent = contextualDataModel, _key = '';
1448
+ if (dataRef === null) {
1449
+ _data = NullDataValue;
1450
+ }
1451
+ else if (dataRef !== undefined && !this.repeatable) {
1452
+ if (this._tokens.length === 0) {
1453
+ this._tokens = tokenize(dataRef);
1474
1454
  }
1475
- for (let i = 0; i < this._jsonModel.initialItems; i++) {
1476
- let child;
1477
- if (mode === 'restore') {
1478
- let itemTemplate = this._itemTemplate;
1479
- if (i < this._jsonModel.items.length) {
1480
- itemTemplate = deepClone(items[i]);
1481
- itemTemplate.repeatable = undefined;
1482
- }
1483
- child = this._addChild(itemTemplate, undefined, i > this._jsonModel.items.length - 1, mode);
1484
- }
1485
- else {
1486
- child = this._addChild(this._itemTemplate, undefined, i > this._jsonModel.items.length - 1);
1487
- }
1488
- if (mode === 'create') {
1489
- items[0].id = child.id;
1455
+ let searchData = contextualDataModel;
1456
+ if (this._tokens[0].type === TOK_GLOBAL) {
1457
+ searchData = this.form.getDataNode();
1458
+ }
1459
+ else if (this._tokens[0].type === TOK_REPEATABLE) {
1460
+ let repeatRoot = this.parent;
1461
+ while (!repeatRoot.repeatable && repeatRoot !== this.form) {
1462
+ repeatRoot = repeatRoot.parent;
1490
1463
  }
1491
- child._initialize(mode);
1464
+ searchData = repeatRoot.getDataNode();
1465
+ }
1466
+ if (typeof searchData !== 'undefined') {
1467
+ const name = this._tokens[this._tokens.length - 1].value;
1468
+ const create = this.defaultDataModel(name);
1469
+ _data = resolveData(searchData, this._tokens, create);
1470
+ _parent = resolveData(searchData, this._tokens.slice(0, -1));
1471
+ _key = name;
1492
1472
  }
1493
1473
  }
1494
- else if (items.length > 0) {
1495
- items.forEach((item) => {
1496
- if (this.isSiteContainer(item)) {
1497
- this._initializeSiteContainer(item);
1498
- }
1499
- else if (this.isAFormField(item)) {
1500
- const child = this._addChild(item, undefined, false, mode);
1501
- child._initialize(mode);
1474
+ else {
1475
+ if (contextualDataModel !== NullDataValue && staticFields.indexOf(this.fieldType) === -1) {
1476
+ _parent = contextualDataModel;
1477
+ const name = this._jsonModel.name || '';
1478
+ const key = contextualDataModel.$type === 'array' ? this.index : name;
1479
+ _key = key;
1480
+ if (key !== '') {
1481
+ const create = this.defaultDataModel(key);
1482
+ if (create !== undefined) {
1483
+ _data = contextualDataModel.$getDataNode(key);
1484
+ if (_data === undefined) {
1485
+ _data = create;
1486
+ contextualDataModel.$addDataNode(key, _data);
1487
+ }
1488
+ }
1502
1489
  }
1503
1490
  else {
1504
- this.form.logger.warn(`A container item was not initialized. ${item}`);
1491
+ _data = undefined;
1505
1492
  }
1506
- });
1507
- this._jsonModel.minItems = this._children.length;
1508
- this._jsonModel.maxItems = this._children.length;
1509
- this._jsonModel.initialItems = this._children.length;
1493
+ }
1510
1494
  }
1511
- else {
1512
- this.form.logger.warn('A container exists with no items.');
1495
+ if (_data) {
1496
+ if (!this.isContainer && _parent !== NullDataValue && _data !== NullDataValue) {
1497
+ _data = _data?.$convertToDataValue();
1498
+ _parent.$addDataNode(_key, _data, true);
1499
+ }
1500
+ _data?.$bindToField(this);
1501
+ this._data = _data;
1513
1502
  }
1514
- this.setupRuleNode();
1515
1503
  }
1516
- _initializeSiteContainer(item) {
1517
- Object.entries(item[':items']).forEach(([key, value]) => {
1518
- if (this.isAFormField(value)) {
1519
- const child = this._addChild(value);
1520
- child._initialize();
1521
- }
1522
- else if (this.isSiteContainer(value)) {
1523
- return this._initializeSiteContainer(value);
1524
- }
1525
- });
1504
+ _data;
1505
+ getDataNode() {
1506
+ return this._data;
1526
1507
  }
1527
- addItem(action) {
1528
- if ((action.type === 'addItem' || action.type == 'addInstance') && this._itemTemplate != null) {
1529
- if ((this._jsonModel.maxItems === -1) || (this._children.length < this._jsonModel.maxItems)) {
1530
- const dataNode = this.getDataNode();
1531
- let instanceIndex = action.payload;
1532
- const retVal = this._addChild(this._itemTemplate, action.payload, true);
1533
- if (typeof instanceIndex !== 'number' || instanceIndex > this._children.length) {
1534
- instanceIndex = this._children.length;
1535
- }
1536
- const _data = retVal.defaultDataModel(instanceIndex);
1537
- if (_data) {
1538
- dataNode.$addDataNode(instanceIndex, _data);
1539
- }
1540
- retVal._initialize('create');
1541
- this.notifyDependents(propertyChange('items', retVal.getState(), null));
1542
- retVal.dispatch(new Initialize());
1543
- retVal.dispatch(new ExecuteRule());
1544
- for (let i = instanceIndex + 1; i < this._children.length; i++) {
1545
- this._children[i].dispatch(new ExecuteRule());
1546
- }
1547
- }
1508
+ get lang() {
1509
+ if (this._jsonModel.lang) {
1510
+ this._lang = this._jsonModel.lang;
1548
1511
  }
1549
- }
1550
- removeItem(action) {
1551
- if ((action.type === 'removeItem' || action.type == 'removeInstance') && this._itemTemplate != null) {
1552
- if (this._children.length == 0) {
1553
- return;
1554
- }
1555
- let instanceIndex = action.payload;
1556
- if (typeof instanceIndex !== 'number') {
1557
- instanceIndex = this._children.length - 1;
1512
+ if (!this._lang) {
1513
+ if (this.parent) {
1514
+ this._lang = this.parent.lang;
1558
1515
  }
1559
- const state = this._children[instanceIndex].getState();
1560
- if (this._children.length > this._jsonModel.minItems) {
1561
- this._childrenReference.pop();
1562
- this._children.splice(instanceIndex, 1);
1563
- this.getDataNode().$removeDataNode(instanceIndex);
1564
- for (let i = instanceIndex; i < this._children.length; i++) {
1565
- this._children[i].dispatch(new ExecuteRule());
1566
- }
1567
- this.notifyDependents(propertyChange('items', null, state));
1516
+ else {
1517
+ this._lang = Intl.DateTimeFormat().resolvedOptions().locale;
1568
1518
  }
1569
1519
  }
1520
+ return this._lang;
1570
1521
  }
1571
- queueEvent(action) {
1572
- super.queueEvent(action);
1573
- if (action.metadata?.dispatch) {
1574
- this.items.forEach(x => {
1575
- x.queueEvent(action);
1576
- });
1522
+ get properties() {
1523
+ return this._jsonModel.properties || {};
1524
+ }
1525
+ set properties(p) {
1526
+ this._setProperty('properties', { ...p });
1527
+ }
1528
+ getNonTransparentParent() {
1529
+ let nonTransparentParent = this.parent;
1530
+ while (nonTransparentParent != null && nonTransparentParent.isTransparent()) {
1531
+ nonTransparentParent = nonTransparentParent.parent;
1577
1532
  }
1533
+ return nonTransparentParent;
1578
1534
  }
1579
- reset() {
1580
- if (this.type === 'array' || isRepeatable$1(this._jsonModel)) {
1581
- if (this.items.length > this._jsonModel.initialItems) {
1582
- const itemsToBeRemoved = this.items.length - this._jsonModel.initialItems;
1583
- for (let i = 0; i < itemsToBeRemoved; i++) {
1584
- this.dispatch(new RemoveItem());
1585
- }
1586
- }
1535
+ _initialize(mode) {
1536
+ if (typeof this._data === 'undefined') {
1537
+ let dataNode, parent = this.parent;
1538
+ do {
1539
+ dataNode = parent.getDataNode();
1540
+ parent = parent.parent;
1541
+ } while (dataNode === undefined);
1542
+ this._bindToDataModel(dataNode);
1587
1543
  }
1588
- this.items.forEach(x => {
1589
- x.reset();
1590
- });
1591
1544
  }
1592
- validate() {
1593
- return this.items.flatMap(x => {
1594
- return x.validate();
1595
- }).filter(x => x.fieldName !== '');
1545
+ _applyUpdates(propNames, updates) {
1546
+ return propNames.reduce((acc, propertyName) => {
1547
+ const currentValue = updates[propertyName];
1548
+ const changes = this._setProperty(propertyName, currentValue, false);
1549
+ if (changes.length > 0) {
1550
+ acc[propertyName] = changes[0];
1551
+ }
1552
+ return acc;
1553
+ }, {});
1596
1554
  }
1597
- dispatch(action) {
1598
- super.dispatch(action);
1555
+ get qualifiedName() {
1556
+ if (this.isTransparent()) {
1557
+ return null;
1558
+ }
1559
+ if (this[qualifiedName] !== null) {
1560
+ return this[qualifiedName];
1561
+ }
1562
+ const parent = this.getNonTransparentParent();
1563
+ if (parent && parent.type === 'array') {
1564
+ this[qualifiedName] = `${parent.qualifiedName}[${this.index}]`;
1565
+ }
1566
+ else {
1567
+ this[qualifiedName] = `${parent.qualifiedName}.${this.name}`;
1568
+ }
1569
+ return this[qualifiedName];
1599
1570
  }
1600
- importData(contextualDataModel) {
1601
- this._bindToDataModel(contextualDataModel);
1602
- const dataNode = this.getDataNode() || contextualDataModel;
1603
- this.syncDataAndFormModel(dataNode);
1571
+ focus() {
1572
+ if (this.parent) {
1573
+ this.parent.activeChild = this;
1574
+ }
1604
1575
  }
1605
- syncDataAndFormModel(contextualDataModel) {
1606
- if (contextualDataModel?.$type === 'array' && this._itemTemplate != null) {
1607
- const dataLength = contextualDataModel?.$value.length;
1608
- const itemsLength = this._children.length;
1609
- const maxItems = this._jsonModel.maxItems === -1 ? dataLength : this._jsonModel.maxItems;
1610
- const minItems = this._jsonModel.minItems;
1611
- let items2Add = Math.min(dataLength - itemsLength, maxItems - itemsLength);
1612
- const items2Remove = Math.min(itemsLength - dataLength, itemsLength - minItems);
1613
- while (items2Add > 0) {
1614
- items2Add--;
1615
- const child = this._addChild(this._itemTemplate);
1616
- child._initialize('create');
1576
+ _getDefaults() {
1577
+ return {};
1578
+ }
1579
+ _applyDefaultsInModel() {
1580
+ Object.entries(this._getDefaults()).map(([key, value]) => {
1581
+ if (this._jsonModel[key] === undefined && value !== undefined) {
1582
+ this._jsonModel[key] = value;
1617
1583
  }
1618
- if (items2Remove > 0) {
1619
- this._children.splice(dataLength, items2Remove);
1620
- for (let i = 0; i < items2Remove; i++) {
1621
- this._childrenReference.pop();
1584
+ else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1585
+ Object.keys(value).forEach((keyOfValue) => {
1586
+ if (this._jsonModel[key][keyOfValue] === undefined) {
1587
+ this._jsonModel[key][keyOfValue] = value[keyOfValue];
1588
+ }
1589
+ });
1590
+ }
1591
+ });
1592
+ }
1593
+ }
1594
+ __decorate([
1595
+ dependencyTracked()
1596
+ ], BaseNode.prototype, "index", null);
1597
+ __decorate([
1598
+ dependencyTracked()
1599
+ ], BaseNode.prototype, "description", null);
1600
+ __decorate([
1601
+ dependencyTracked()
1602
+ ], BaseNode.prototype, "visible", null);
1603
+ __decorate([
1604
+ dependencyTracked()
1605
+ ], BaseNode.prototype, "label", null);
1606
+ __decorate([
1607
+ dependencyTracked()
1608
+ ], BaseNode.prototype, "properties", null);
1609
+
1610
+ class Scriptable extends BaseNode {
1611
+ _events = {};
1612
+ _rules = {};
1613
+ getRules() {
1614
+ return typeof this._jsonModel.rules !== 'object' ? {} : this._jsonModel.rules;
1615
+ }
1616
+ getCompiledRule(eName, rule) {
1617
+ if (!(eName in this._rules)) {
1618
+ const eString = rule || this.getRules()[eName];
1619
+ if (typeof eString === 'string' && eString.length > 0) {
1620
+ try {
1621
+ this._rules[eName] = this.ruleEngine.compileRule(eString, this.lang);
1622
+ }
1623
+ catch (e) {
1624
+ this.form.logger.error(`Unable to compile rule \`"${eName}" : "${eString}"\` Exception : ${e}`);
1622
1625
  }
1623
1626
  }
1627
+ else {
1628
+ throw new Error(`only expression strings are supported. ${typeof (eString)} types are not supported`);
1629
+ }
1624
1630
  }
1625
- this._children.forEach(x => {
1626
- x.importData(contextualDataModel);
1627
- });
1628
- }
1629
- get activeChild() {
1630
- return this._activeChild;
1631
+ return this._rules[eName];
1631
1632
  }
1632
- set activeChild(c) {
1633
- if (c !== this._activeChild) {
1634
- let activeChild = this._activeChild;
1635
- while (activeChild instanceof Container) {
1636
- const temp = activeChild.activeChild;
1637
- activeChild.activeChild = null;
1638
- activeChild = temp;
1633
+ getCompiledEvent(eName) {
1634
+ if (!(eName in this._events)) {
1635
+ let eString = this._jsonModel.events?.[eName];
1636
+ if (typeof eString === 'string' && eString.length > 0) {
1637
+ eString = [eString];
1639
1638
  }
1640
- const change = propertyChange('activeChild', c, this._activeChild);
1641
- this._activeChild = c;
1642
- if (this.parent && c !== null) {
1643
- this.parent.activeChild = this;
1639
+ if (typeof eString !== 'undefined' && eString.length > 0) {
1640
+ this._events[eName] = eString.map(x => {
1641
+ try {
1642
+ return this.ruleEngine.compileRule(x, this.lang);
1643
+ }
1644
+ catch (e) {
1645
+ this.form.logger.error(`Unable to compile expression \`"${eName}" : "${eString}"\` Exception : ${e}`);
1646
+ }
1647
+ return null;
1648
+ }).filter(x => x !== null);
1644
1649
  }
1645
- this._jsonModel.activeChild = c?.id;
1646
- this.notifyDependents(change);
1647
- }
1648
- }
1649
- get enabled() {
1650
- if (this.parent?.enabled !== undefined) {
1651
- return !this.parent?.enabled ? false : this._jsonModel.enabled;
1652
- }
1653
- else {
1654
- return this._jsonModel.enabled;
1655
- }
1656
- }
1657
- set enabled(e) {
1658
- this._setProperty('enabled', e, true, this.notifyChildren);
1659
- }
1660
- get readOnly() {
1661
- if (this.parent?.readOnly !== undefined) {
1662
- return this.parent.readOnly ? true : this._jsonModel.readOnly;
1663
- }
1664
- else {
1665
- return this._jsonModel.readOnly;
1666
1650
  }
1651
+ return this._events[eName] || [];
1667
1652
  }
1668
- set readOnly(e) {
1669
- this._setProperty('readOnly', e, true, this.notifyChildren);
1653
+ applyUpdates(updates) {
1654
+ Object.entries(updates).forEach(([key, value]) => {
1655
+ if (key in editableProperties || (key in this && typeof this[key] !== 'function')) {
1656
+ try {
1657
+ this[key] = value;
1658
+ }
1659
+ catch (e) {
1660
+ console.error(e);
1661
+ }
1662
+ }
1663
+ });
1670
1664
  }
1671
- notifyChildren(action) {
1672
- if (action.payload !== undefined && action.payload.changes !== undefined) {
1673
- for (const change of action.payload.changes) {
1674
- if (change.propertyName !== undefined && notifyChildrenAttributes.includes(change.propertyName)) {
1675
- this.items.forEach((child) => {
1676
- this.notifyDependents.call(child, propertyChange(change.propertyName, child.getState()[change.propertyName], null));
1677
- if (child.fieldType === 'panel') {
1678
- this.notifyChildren.call(child, action);
1665
+ executeAllRules(context) {
1666
+ const entries = Object.entries(this.getRules());
1667
+ if (entries.length > 0) {
1668
+ const scope = this.getExpressionScope();
1669
+ entries.forEach(([prop, rule]) => {
1670
+ const node = this.getCompiledRule(prop, rule);
1671
+ if (node) {
1672
+ const newVal = this.ruleEngine.execute(node, scope, context, true);
1673
+ if (editableProperties.indexOf(prop) > -1) {
1674
+ const oldAndNewValueAreEmpty = this.isEmpty() && this.isEmpty(newVal) && prop === 'value';
1675
+ if (!oldAndNewValueAreEmpty) {
1676
+ this[prop] = newVal;
1679
1677
  }
1680
- });
1678
+ }
1679
+ else {
1680
+ this.form.logger.warn(`${prop} is not a valid editable property.`);
1681
+ }
1681
1682
  }
1682
- }
1683
+ });
1683
1684
  }
1684
1685
  }
1685
- }
1686
- __decorate([
1687
- dependencyTracked()
1688
- ], Container.prototype, "maxItems", null);
1689
- __decorate([
1690
- dependencyTracked()
1691
- ], Container.prototype, "minItems", null);
1692
- __decorate([
1693
- dependencyTracked()
1694
- ], Container.prototype, "activeChild", null);
1695
-
1696
- class Node {
1697
- _jsonModel;
1698
- constructor(inputModel) {
1699
- this._jsonModel = {
1700
- ...inputModel
1686
+ getExpressionScope() {
1687
+ const parent = this.getNonTransparentParent();
1688
+ const target = {
1689
+ self: this.getRuleNode(),
1690
+ siblings: parent?.ruleNodeReference() || {}
1701
1691
  };
1692
+ const scope = new Proxy(target, {
1693
+ get: (target, prop) => {
1694
+ if (prop === Symbol.toStringTag) {
1695
+ return 'Object';
1696
+ }
1697
+ if (typeof prop === 'string' && prop.startsWith('$')) {
1698
+ const retValue = target.self[prop];
1699
+ if (retValue instanceof BaseNode) {
1700
+ return retValue.getRuleNode();
1701
+ }
1702
+ else if (retValue instanceof Array) {
1703
+ return retValue.map(r => r instanceof BaseNode ? r.getRuleNode() : r);
1704
+ }
1705
+ else {
1706
+ return retValue;
1707
+ }
1708
+ }
1709
+ else {
1710
+ if (prop in target.siblings) {
1711
+ return target.siblings[prop];
1712
+ }
1713
+ else {
1714
+ return target.self[prop];
1715
+ }
1716
+ }
1717
+ },
1718
+ has: (target, prop) => {
1719
+ prop = prop;
1720
+ const selfPropertyOrChild = target.self[prop];
1721
+ const sibling = target.siblings[prop];
1722
+ return typeof selfPropertyOrChild != 'undefined' || typeof sibling != 'undefined';
1723
+ }
1724
+ });
1725
+ return scope;
1702
1726
  }
1703
- getP(key, def) {
1704
- return getProperty(this._jsonModel, key, def);
1705
- }
1706
- get isContainer() {
1707
- return false;
1727
+ executeEvent(context, node) {
1728
+ let updates;
1729
+ if (node) {
1730
+ updates = this.ruleEngine.execute(node, this.getExpressionScope(), context);
1731
+ }
1732
+ if (typeof updates !== 'undefined' && updates != null) {
1733
+ this.applyUpdates(updates);
1734
+ }
1708
1735
  }
1709
- }
1710
-
1711
- class FormMetaData extends Node {
1712
- get version() {
1713
- return this.getP('version', '');
1736
+ executeRule(event, context) {
1737
+ if (typeof event.payload.ruleName === 'undefined') {
1738
+ this.executeAllRules(context);
1739
+ }
1714
1740
  }
1715
- get grammar() {
1716
- return this.getP('grammar', '');
1741
+ executeExpression(expr) {
1742
+ const ruleContext = {
1743
+ 'form': this.form,
1744
+ '$form': this.form.getRuleNode(),
1745
+ '$field': this.getRuleNode(),
1746
+ 'field': this
1747
+ };
1748
+ const node = this.ruleEngine.compileRule(expr, this.lang);
1749
+ return this.ruleEngine.execute(node, this.getExpressionScope(), ruleContext);
1717
1750
  }
1718
- }
1719
-
1720
- class SubmitMetaData {
1721
- lang;
1722
- captchaInfo;
1723
- constructor(lang = '', captchaInfo) {
1724
- this.lang = lang;
1725
- this.captchaInfo = captchaInfo;
1751
+ executeAction(action) {
1752
+ const context = {
1753
+ 'form': this.form,
1754
+ '$form': this.form.getRuleNode(),
1755
+ '$field': this.getRuleNode(),
1756
+ 'field': this,
1757
+ '$event': {
1758
+ type: action.type,
1759
+ payload: action.payload,
1760
+ target: this.getRuleNode()
1761
+ }
1762
+ };
1763
+ const eventName = action.isCustomEvent ? `custom:${action.type}` : action.type;
1764
+ const funcName = action.isCustomEvent ? `custom_${action.type}` : action.type;
1765
+ const node = this.getCompiledEvent(eventName);
1766
+ if (funcName in this && typeof this[funcName] === 'function') {
1767
+ this[funcName](action, context);
1768
+ }
1769
+ node.forEach((n) => this.executeEvent(context, n));
1770
+ this.notifyDependents(action);
1726
1771
  }
1727
1772
  }
1728
1773
 
1729
- const levels = {
1730
- off: 0,
1731
- debug: 1,
1732
- info: 2,
1733
- warn: 3,
1734
- error: 4
1735
- };
1736
- class Logger {
1737
- debug(msg) {
1738
- this.log(msg, 'debug');
1739
- }
1740
- info(msg) {
1741
- this.log(msg, 'info');
1742
- }
1743
- warn(msg) {
1744
- this.log(msg, 'warn');
1745
- }
1746
- error(msg) {
1747
- this.log(msg, 'error');
1774
+ const notifyChildrenAttributes = [
1775
+ 'readOnly', 'enabled'
1776
+ ];
1777
+ class Container extends Scriptable {
1778
+ _children = [];
1779
+ _childrenReference;
1780
+ _itemTemplate = null;
1781
+ fieldFactory;
1782
+ constructor(json, _options) {
1783
+ super(json, { form: _options.form, parent: _options.parent, mode: _options.mode });
1784
+ this.fieldFactory = _options.fieldFactory;
1748
1785
  }
1749
- log(msg, level) {
1750
- if (this.logLevel !== 0 && this.logLevel <= levels[level]) {
1751
- console[level](msg);
1752
- }
1786
+ _getDefaults() {
1787
+ return {
1788
+ ...super._getDefaults(),
1789
+ enabled: true,
1790
+ readOnly: false
1791
+ };
1753
1792
  }
1754
- logLevel;
1755
- constructor(logLevel = 'off') {
1756
- this.logLevel = levels[logLevel];
1793
+ ruleNodeReference() {
1794
+ return this._childrenReference;
1757
1795
  }
1758
- }
1759
-
1760
- class EventNode {
1761
- _node;
1762
- _event;
1763
- constructor(_node, _event) {
1764
- this._node = _node;
1765
- this._event = _event;
1796
+ get items() {
1797
+ return this._children;
1766
1798
  }
1767
- get node() {
1768
- return this._node;
1799
+ get maxItems() {
1800
+ return this._jsonModel.maxItems;
1769
1801
  }
1770
- get event() {
1771
- return this._event;
1802
+ set maxItems(m) {
1803
+ this._jsonModel.maxItems = m;
1804
+ const minItems = this._jsonModel.minItems || 1;
1805
+ const itemsLength = this._children.length;
1806
+ const items2Remove = Math.min(itemsLength - m, itemsLength - minItems);
1807
+ if (items2Remove > 0) {
1808
+ for (let i = 0; i < items2Remove; i++) {
1809
+ this.getDataNode().$removeDataNode(m + i);
1810
+ this._childrenReference.pop();
1811
+ }
1812
+ const elems = this._children.splice(m, items2Remove);
1813
+ this.notifyDependents(propertyChange('items', elems, null));
1814
+ }
1772
1815
  }
1773
- isEqual(that) {
1774
- return that !== null && that !== undefined && this._node == that._node && this._event.type == that._event.type;
1816
+ get minItems() {
1817
+ return this._jsonModel.minItems;
1775
1818
  }
1776
- toString() {
1777
- return this._node.id + '__' + this.event.type;
1819
+ set minItems(m) {
1820
+ this._jsonModel.minItems = m;
1821
+ const itemsLength = this._children.length;
1822
+ const difference = itemsLength - m;
1823
+ const items2Add = Math.abs(difference);
1824
+ if (difference < 0) {
1825
+ const elems = [];
1826
+ for (let i = 0; i < items2Add; i++) {
1827
+ elems.push(this._addChild(this._itemTemplate, null, true));
1828
+ }
1829
+ this.notifyDependents(propertyChange('items', elems, null));
1830
+ }
1778
1831
  }
1779
- valueOf() {
1780
- return this.toString();
1832
+ hasDynamicItems() {
1833
+ return this._itemTemplate != null;
1781
1834
  }
1782
- }
1783
- class EventQueue {
1784
- logger;
1785
- static MAX_EVENT_CYCLE_COUNT = 10;
1786
- _runningEventCount;
1787
- _isProcessing = false;
1788
- _pendingEvents = [];
1789
- constructor(logger = new Logger('off')) {
1790
- this.logger = logger;
1791
- this._runningEventCount = {};
1835
+ get isContainer() {
1836
+ return true;
1792
1837
  }
1793
- get length() {
1794
- return this._pendingEvents.length;
1838
+ _activeChild = null;
1839
+ isSiteContainer(item) {
1840
+ return (':items' in item || 'cqItems' in item) && !('fieldType' in item);
1795
1841
  }
1796
- get isProcessing() {
1797
- return this._isProcessing;
1842
+ isAFormField(item) {
1843
+ return ('fieldType' in item || 'id' in item || 'name' in item || 'dataRef' in item || 'type' in item);
1798
1844
  }
1799
- isQueued(node, event) {
1800
- const evntNode = new EventNode(node, event);
1801
- return this._pendingEvents.find(x => evntNode.isEqual(x)) !== undefined;
1845
+ _getFormAndSitesState(isRepeatableChild = false, forRestore = false) {
1846
+ return this._jsonModel.items ? this._jsonModel.items.map((x) => {
1847
+ if (this.isSiteContainer(x)) {
1848
+ const newObjWithId = {
1849
+ ...(x?.id ? { id: this.form.getUniqueId() } : {})
1850
+ };
1851
+ return {
1852
+ ...x,
1853
+ ...newObjWithId,
1854
+ ':items': this.walkSiteContainerItems(x)
1855
+ };
1856
+ }
1857
+ else if (this.isAFormField(x)) {
1858
+ return { ...this.form.getElement(x?.id).getState(isRepeatableChild, forRestore) };
1859
+ }
1860
+ else {
1861
+ return x;
1862
+ }
1863
+ }) : [];
1802
1864
  }
1803
- queue(node, events, priority = false) {
1804
- if (!node || !events) {
1805
- return;
1865
+ getItemsState(isRepeatableChild = false, forRestore = false) {
1866
+ if (this._jsonModel.type === 'array' || isRepeatable$1(this._jsonModel) || isRepeatableChild) {
1867
+ if (isRepeatableChild) {
1868
+ return this._getFormAndSitesState(isRepeatableChild, forRestore);
1869
+ }
1870
+ else {
1871
+ return this._children.map(x => {
1872
+ return { ...x.getState(true, forRestore) };
1873
+ });
1874
+ }
1806
1875
  }
1807
- if (!(events instanceof Array)) {
1808
- events = [events];
1876
+ else {
1877
+ return this._getFormAndSitesState(isRepeatableChild, forRestore);
1809
1878
  }
1810
- events.forEach(e => {
1811
- const evntNode = new EventNode(node, e);
1812
- const counter = this._runningEventCount[evntNode.valueOf()] || 0;
1813
- if (counter < EventQueue.MAX_EVENT_CYCLE_COUNT) {
1814
- this.logger.info(`Queued event : ${e.type} node: ${node.id} - ${node.name}`);
1815
- if (priority) {
1816
- const index = this._isProcessing ? 1 : 0;
1817
- this._pendingEvents.splice(index, 0, evntNode);
1879
+ }
1880
+ getState(isRepeatableChild = false, forRestore = false) {
1881
+ return {
1882
+ ...super.getState(forRestore),
1883
+ ...(forRestore ? {
1884
+ ':items': undefined,
1885
+ ':itemsOrder': undefined
1886
+ } : {}),
1887
+ items: this.getItemsState(isRepeatableChild, forRestore),
1888
+ enabled: this.enabled,
1889
+ readOnly: this.readOnly
1890
+ };
1891
+ }
1892
+ _createChild(child, options) {
1893
+ return this.fieldFactory.createField(child, options);
1894
+ }
1895
+ walkSiteContainerItems(x) {
1896
+ return Object.fromEntries(Object.entries(x[':items']).map(([key, value]) => {
1897
+ if (this.isAFormField(value)) {
1898
+ return [key, this.form.getElement(value?.id).getState()];
1899
+ }
1900
+ else if (this.isSiteContainer(value)) {
1901
+ return this.walkSiteContainerItems(value);
1902
+ }
1903
+ else {
1904
+ if (typeof value === 'object') {
1905
+ const newObjWithId = {
1906
+ ...(value?.id ? { id: this.form.getUniqueId() } : {})
1907
+ };
1908
+ return [key, {
1909
+ ...value,
1910
+ ...newObjWithId
1911
+ }
1912
+ ];
1818
1913
  }
1819
1914
  else {
1820
- this._pendingEvents.push(evntNode);
1915
+ return [key, value];
1821
1916
  }
1822
- this._runningEventCount[evntNode.valueOf()] = counter + 1;
1823
1917
  }
1824
- else {
1825
- this.logger.info(`Skipped queueing event : ${e.type} node: ${node.id} - ${node.name} with count=${counter}`);
1826
- }
1827
- });
1918
+ }));
1828
1919
  }
1829
- empty() {
1830
- this._pendingEvents = [];
1920
+ _addChildToRuleNode(child, options) {
1921
+ const self = this;
1922
+ const { parent = this } = options;
1923
+ const name = parent.type == 'array' ? parent._children.length + '' : child.name || '';
1924
+ if (name.length > 0) {
1925
+ Object.defineProperty(parent._childrenReference, name, {
1926
+ get: () => {
1927
+ if (child.isContainer && child.hasDynamicItems()) {
1928
+ self.ruleEngine.trackDependency(child);
1929
+ }
1930
+ if (self.hasDynamicItems()) {
1931
+ self.ruleEngine.trackDependency(self);
1932
+ if (this._children[name] !== undefined) {
1933
+ return this._children[name].getRuleNode();
1934
+ }
1935
+ }
1936
+ else {
1937
+ return child.getRuleNode();
1938
+ }
1939
+ },
1940
+ configurable: true,
1941
+ enumerable: true
1942
+ });
1943
+ }
1831
1944
  }
1832
- runPendingQueue() {
1833
- if (this._isProcessing) {
1834
- return;
1945
+ _addChild(itemJson, index, cloneIds = false, mode = 'create') {
1946
+ let nonTransparentParent = this;
1947
+ while (nonTransparentParent != null && nonTransparentParent.isTransparent()) {
1948
+ nonTransparentParent = nonTransparentParent.parent;
1835
1949
  }
1836
- this._isProcessing = true;
1837
- while (this._pendingEvents.length > 0) {
1838
- const e = this._pendingEvents[0];
1839
- this.logger.info(`Dequeued event : ${e.event.type} node: ${e.node.id} - ${e.node.name}`);
1840
- e.node.executeAction(e.event);
1841
- this._pendingEvents.shift();
1950
+ if (typeof index !== 'number' || index > nonTransparentParent._children.length) {
1951
+ index = this._children.length;
1842
1952
  }
1843
- this._runningEventCount = {};
1844
- this._isProcessing = false;
1845
- }
1846
- }
1847
-
1848
- class FileObject {
1849
- data;
1850
- mediaType = 'application/octet-stream';
1851
- name = 'unknown';
1852
- size = 0;
1853
- constructor(init) {
1854
- Object.assign(this, init);
1855
- }
1856
- get type() {
1857
- return this.mediaType;
1858
- }
1859
- toJSON() {
1860
- return {
1861
- 'name': this.name,
1862
- 'size': this.size,
1863
- 'mediaType': this.mediaType,
1864
- 'data': this.data.toString()
1953
+ const form = this.form;
1954
+ const itemTemplate = {
1955
+ index,
1956
+ ...deepClone(itemJson, cloneIds ? () => { return form.getUniqueId(); } : undefined)
1865
1957
  };
1866
- }
1867
- equals(obj) {
1868
- return (this.data === obj.data &&
1869
- this.mediaType === obj.mediaType &&
1870
- this.name === obj.name &&
1871
- this.size === obj.size);
1872
- }
1873
- }
1874
-
1875
- const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'.split('');
1876
- const fileSizeRegex = /^(\d*\.?\d+)(\\?(?=[KMGT])([KMGT])(?:i?B)?|B?)$/i;
1877
- const randomWord = (l) => {
1878
- const ret = [];
1879
- for (let i = 0; i <= l; i++) {
1880
- let randIndex;
1881
- if (i === 0) {
1882
- randIndex = Math.floor(Math.random() * (chars.length - 11));
1958
+ const retVal = this._createChild(itemTemplate, { parent: this, form: this.form, mode });
1959
+ itemJson.id = retVal.id;
1960
+ this.form.fieldAdded(retVal);
1961
+ this._addChildToRuleNode(retVal, { parent: nonTransparentParent });
1962
+ if (index === this._children.length) {
1963
+ this._children.push(retVal);
1964
+ }
1965
+ else {
1966
+ this._children.splice(index, 0, retVal);
1967
+ }
1968
+ return retVal;
1969
+ }
1970
+ indexOf(f) {
1971
+ return this._children.indexOf(f);
1972
+ }
1973
+ defaultDataModel(name) {
1974
+ const type = this._jsonModel.type || undefined;
1975
+ if (type === undefined) {
1976
+ return undefined;
1883
1977
  }
1884
1978
  else {
1885
- randIndex = Math.floor(Math.random() * (chars.length));
1979
+ const instance = type === 'array' ? [] : {};
1980
+ return new DataGroup(name, instance, type);
1886
1981
  }
1887
- ret.push(chars[randIndex]);
1888
1982
  }
1889
- return ret.join('');
1890
- };
1891
- const getAttachments = (input, excludeUnbound = false) => {
1892
- const items = input.items || [];
1893
- return items?.reduce((acc, item) => {
1894
- if (excludeUnbound && item.dataRef === null) {
1895
- return acc;
1983
+ _canHaveRepeatingChildren(mode = 'create') {
1984
+ const items = this._jsonModel.items;
1985
+ return this._jsonModel.type == 'array' && this.getDataNode() != null &&
1986
+ (items.length === 1 || (items[0].repeatable == true && mode === 'restore'));
1987
+ }
1988
+ _initialize(mode) {
1989
+ super._initialize(mode);
1990
+ const items = this._jsonModel.items || [];
1991
+ this._childrenReference = this._jsonModel.type == 'array' ? [] : {};
1992
+ if (this._canHaveRepeatingChildren(mode)) {
1993
+ this._itemTemplate = deepClone(items[0]);
1994
+ if (mode === 'restore') {
1995
+ this._itemTemplate.repeatable = undefined;
1996
+ }
1997
+ if (typeof (this._jsonModel.minItems) !== 'number') {
1998
+ this._jsonModel.minItems = 0;
1999
+ }
2000
+ if (typeof (this._jsonModel.maxItems) !== 'number') {
2001
+ this._jsonModel.maxItems = -1;
2002
+ }
2003
+ if (typeof (this._jsonModel.initialItems) !== 'number') {
2004
+ this._jsonModel.initialItems = Math.max(1, this._jsonModel.minItems);
2005
+ }
2006
+ for (let i = 0; i < this._jsonModel.initialItems; i++) {
2007
+ let child;
2008
+ if (mode === 'restore') {
2009
+ let itemTemplate = this._itemTemplate;
2010
+ if (i < this._jsonModel.items.length) {
2011
+ itemTemplate = deepClone(items[i]);
2012
+ itemTemplate.repeatable = undefined;
2013
+ }
2014
+ child = this._addChild(itemTemplate, undefined, i > this._jsonModel.items.length - 1, mode);
2015
+ }
2016
+ else {
2017
+ child = this._addChild(this._itemTemplate, undefined, i > this._jsonModel.items.length - 1);
2018
+ }
2019
+ if (mode === 'create') {
2020
+ items[0].id = child.id;
2021
+ }
2022
+ child._initialize(mode);
2023
+ }
1896
2024
  }
1897
- let ret = null;
1898
- if (item.isContainer) {
1899
- ret = getAttachments(item, excludeUnbound);
2025
+ else if (items.length > 0) {
2026
+ items.forEach((item) => {
2027
+ if (this.isSiteContainer(item)) {
2028
+ this._initializeSiteContainer(item);
2029
+ }
2030
+ else if (this.isAFormField(item)) {
2031
+ const child = this._addChild(item, undefined, false, mode);
2032
+ child._initialize(mode);
2033
+ }
2034
+ else {
2035
+ this.form.logger.warn(`A container item was not initialized. ${item}`);
2036
+ }
2037
+ });
2038
+ this._jsonModel.minItems = this._children.length;
2039
+ this._jsonModel.maxItems = this._children.length;
2040
+ this._jsonModel.initialItems = this._children.length;
1900
2041
  }
1901
2042
  else {
1902
- if (isFile(item.getState())) {
1903
- ret = {};
1904
- const name = item.name || '';
1905
- const dataRef = (item.dataRef != null)
1906
- ? item.dataRef
1907
- : (name.length > 0 ? item.name : undefined);
1908
- if (item.value instanceof Array) {
1909
- ret[item.id] = item.value.map((x) => {
1910
- return { ...x, 'dataRef': dataRef };
1911
- });
2043
+ this.form.logger.warn('A container exists with no items.');
2044
+ }
2045
+ this.setupRuleNode();
2046
+ }
2047
+ _initializeSiteContainer(item) {
2048
+ Object.entries(item[':items']).forEach(([key, value]) => {
2049
+ if (this.isAFormField(value)) {
2050
+ const child = this._addChild(value);
2051
+ child._initialize();
2052
+ }
2053
+ else if (this.isSiteContainer(value)) {
2054
+ return this._initializeSiteContainer(value);
2055
+ }
2056
+ });
2057
+ }
2058
+ addItem(action) {
2059
+ if ((action.type === 'addItem' || action.type == 'addInstance') && this._itemTemplate != null) {
2060
+ if ((this._jsonModel.maxItems === -1) || (this._children.length < this._jsonModel.maxItems)) {
2061
+ const dataNode = this.getDataNode();
2062
+ let instanceIndex = action.payload;
2063
+ const retVal = this._addChild(this._itemTemplate, action.payload, true);
2064
+ if (typeof instanceIndex !== 'number' || instanceIndex > this._children.length) {
2065
+ instanceIndex = this._children.length;
1912
2066
  }
1913
- else if (item.value != null) {
1914
- ret[item.id] = { ...item.value, 'dataRef': dataRef };
2067
+ const _data = retVal.defaultDataModel(instanceIndex);
2068
+ if (_data) {
2069
+ dataNode.$addDataNode(instanceIndex, _data);
2070
+ }
2071
+ retVal._initialize('create');
2072
+ this.notifyDependents(propertyChange('items', retVal.getState(), null));
2073
+ retVal.dispatch(new Initialize());
2074
+ retVal.dispatch(new ExecuteRule());
2075
+ for (let i = instanceIndex + 1; i < this._children.length; i++) {
2076
+ this._children[i].dispatch(new ExecuteRule());
1915
2077
  }
1916
2078
  }
1917
2079
  }
1918
- return Object.assign(acc, ret);
1919
- }, {});
1920
- };
1921
- const getFileSizeInBytes = (str) => {
1922
- let retVal = 0;
1923
- if (typeof str === 'string') {
1924
- const matches = fileSizeRegex.exec(str.trim());
1925
- if (matches != null) {
1926
- retVal = sizeToBytes(parseFloat(matches[1]), (matches[2] || 'kb').toUpperCase());
1927
- }
1928
2080
  }
1929
- return retVal;
1930
- };
1931
- const sizeToBytes = (size, symbol) => {
1932
- const sizes = { 'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4 };
1933
- const i = Math.pow(1024, sizes[symbol]);
1934
- return Math.round(size * i);
1935
- };
1936
- const IdGenerator = function* (initial = 50) {
1937
- const initialize = function () {
1938
- const arr = [];
1939
- for (let i = 0; i < initial; i++) {
1940
- arr.push(randomWord(10));
1941
- }
1942
- return arr;
1943
- };
1944
- const passedIds = {};
1945
- let ids = initialize();
1946
- do {
1947
- let x = ids.pop();
1948
- while (x in passedIds) {
1949
- if (ids.length === 0) {
1950
- ids = initialize();
2081
+ removeItem(action) {
2082
+ if ((action.type === 'removeItem' || action.type == 'removeInstance') && this._itemTemplate != null) {
2083
+ if (this._children.length == 0) {
2084
+ return;
1951
2085
  }
1952
- x = ids.pop();
1953
- }
1954
- passedIds[x] = true;
1955
- yield ids.pop();
1956
- if (ids.length === 0) {
1957
- ids = initialize();
1958
- }
1959
- } while (ids.length > 0);
1960
- };
1961
- const isDataUrl = (str) => {
1962
- const dataUrlRegex = /^data:([a-z]+\/[a-z0-9-+.]+)?;(?:name=(.*);)?base64,(.*)$/;
1963
- return dataUrlRegex.exec(str.trim()) != null;
1964
- };
1965
- const extractFileInfo = (file) => {
1966
- if (file !== null) {
1967
- let retVal = null;
1968
- if (file instanceof FileObject) {
1969
- retVal = file;
1970
- }
1971
- else if (typeof File !== 'undefined' && file instanceof File) {
1972
- retVal = {
1973
- name: file.name,
1974
- mediaType: file.type,
1975
- size: file.size,
1976
- data: file
1977
- };
1978
- }
1979
- else if (typeof file === 'string' && isDataUrl(file)) {
1980
- const result = dataURItoBlob(file);
1981
- if (result !== null) {
1982
- const { blob, name } = result;
1983
- retVal = {
1984
- name: name,
1985
- mediaType: blob.type,
1986
- size: blob.size,
1987
- data: blob
1988
- };
2086
+ let instanceIndex = action.payload;
2087
+ if (typeof instanceIndex !== 'number') {
2088
+ instanceIndex = this._children.length - 1;
2089
+ }
2090
+ const state = this._children[instanceIndex].getState();
2091
+ if (this._children.length > this._jsonModel.minItems) {
2092
+ this._childrenReference.pop();
2093
+ this._children.splice(instanceIndex, 1);
2094
+ this.getDataNode().$removeDataNode(instanceIndex);
2095
+ for (let i = instanceIndex; i < this._children.length; i++) {
2096
+ this._children[i].dispatch(new ExecuteRule());
2097
+ }
2098
+ this.notifyDependents(propertyChange('items', null, state));
1989
2099
  }
1990
2100
  }
1991
- else {
1992
- let jFile = file;
1993
- try {
1994
- jFile = JSON.parse(file);
1995
- retVal = jFile;
1996
- if (!retVal.mediaType) {
1997
- retVal.mediaType = retVal.type;
2101
+ }
2102
+ queueEvent(action) {
2103
+ super.queueEvent(action);
2104
+ if (action.metadata?.dispatch) {
2105
+ this.items.forEach(x => {
2106
+ x.queueEvent(action);
2107
+ });
2108
+ }
2109
+ }
2110
+ reset() {
2111
+ if (this.type === 'array' || isRepeatable$1(this._jsonModel)) {
2112
+ if (this.items.length > this._jsonModel.initialItems) {
2113
+ const itemsToBeRemoved = this.items.length - this._jsonModel.initialItems;
2114
+ for (let i = 0; i < itemsToBeRemoved; i++) {
2115
+ this.dispatch(new RemoveItem());
1998
2116
  }
1999
2117
  }
2000
- catch (ex) {
2118
+ }
2119
+ this.items.forEach(x => {
2120
+ x.reset();
2121
+ });
2122
+ }
2123
+ validate() {
2124
+ return this.items.flatMap(x => {
2125
+ return x.validate();
2126
+ }).filter(x => x.fieldName !== '');
2127
+ }
2128
+ dispatch(action) {
2129
+ super.dispatch(action);
2130
+ }
2131
+ importData(contextualDataModel) {
2132
+ this._bindToDataModel(contextualDataModel);
2133
+ const dataNode = this.getDataNode() || contextualDataModel;
2134
+ this.syncDataAndFormModel(dataNode);
2135
+ }
2136
+ syncDataAndFormModel(contextualDataModel) {
2137
+ if (contextualDataModel?.$type === 'array' && this._itemTemplate != null) {
2138
+ const dataLength = contextualDataModel?.$value.length;
2139
+ const itemsLength = this._children.length;
2140
+ const maxItems = this._jsonModel.maxItems === -1 ? dataLength : this._jsonModel.maxItems;
2141
+ const minItems = this._jsonModel.minItems;
2142
+ let items2Add = Math.min(dataLength - itemsLength, maxItems - itemsLength);
2143
+ const items2Remove = Math.min(itemsLength - dataLength, itemsLength - minItems);
2144
+ while (items2Add > 0) {
2145
+ items2Add--;
2146
+ const child = this._addChild(this._itemTemplate);
2147
+ child._initialize('create');
2001
2148
  }
2002
- if (typeof jFile?.data === 'string' && isDataUrl(jFile?.data)) {
2003
- const result = dataURItoBlob(jFile?.data);
2004
- if (result !== null) {
2005
- const blob = result.blob;
2006
- retVal = {
2007
- name: jFile?.name,
2008
- mediaType: jFile?.type || jFile?.mediaType,
2009
- size: blob.size,
2010
- data: blob
2011
- };
2149
+ if (items2Remove > 0) {
2150
+ this._children.splice(dataLength, items2Remove);
2151
+ for (let i = 0; i < items2Remove; i++) {
2152
+ this._childrenReference.pop();
2012
2153
  }
2013
2154
  }
2014
- else if (typeof jFile === 'string') {
2015
- const fileName = jFile.split('/').pop();
2016
- retVal = {
2017
- name: fileName,
2018
- mediaType: 'application/octet-stream',
2019
- size: 0,
2020
- data: jFile
2021
- };
2155
+ }
2156
+ this._children.forEach(x => {
2157
+ x.importData(contextualDataModel);
2158
+ });
2159
+ }
2160
+ get activeChild() {
2161
+ return this._activeChild;
2162
+ }
2163
+ set activeChild(c) {
2164
+ if (c !== this._activeChild) {
2165
+ let activeChild = this._activeChild;
2166
+ while (activeChild instanceof Container) {
2167
+ const temp = activeChild.activeChild;
2168
+ activeChild.activeChild = null;
2169
+ activeChild = temp;
2022
2170
  }
2023
- else if (typeof jFile === 'object') {
2024
- retVal = {
2025
- name: jFile?.name,
2026
- mediaType: jFile?.type || jFile?.mediaType,
2027
- size: jFile?.size,
2028
- data: jFile?.data
2029
- };
2171
+ const change = propertyChange('activeChild', c, this._activeChild);
2172
+ this._activeChild = c;
2173
+ if (this.parent && c !== null) {
2174
+ this.parent.activeChild = this;
2030
2175
  }
2176
+ this._jsonModel.activeChild = c?.id;
2177
+ this.notifyDependents(change);
2031
2178
  }
2032
- if (retVal !== null && retVal.data != null) {
2033
- return new FileObject(retVal);
2179
+ }
2180
+ get enabled() {
2181
+ if (this.parent?.enabled !== undefined) {
2182
+ return !this.parent?.enabled ? false : this._jsonModel.enabled;
2183
+ }
2184
+ else {
2185
+ return this._jsonModel.enabled;
2034
2186
  }
2035
- return null;
2036
2187
  }
2037
- else {
2038
- return null;
2188
+ set enabled(e) {
2189
+ this._setProperty('enabled', e, true, this.notifyChildren);
2039
2190
  }
2040
- };
2041
- const dataURItoBlob = (dataURI) => {
2042
- const regex = /^data:([a-z]+\/[a-z0-9-+.]+)?(?:;name=([^;]+))?(;base64)?,(.+)$/;
2043
- const groups = regex.exec(dataURI);
2044
- if (groups !== null) {
2045
- const type = groups[1] || '';
2046
- const name = groups[2] || 'unknown';
2047
- const isBase64 = typeof groups[3] === 'string';
2048
- if (isBase64) {
2049
- const binary = atob(groups[4]);
2050
- const array = [];
2051
- for (let i = 0; i < binary.length; i++) {
2052
- array.push(binary.charCodeAt(i));
2053
- }
2054
- const blob = new window.Blob([new Uint8Array(array)], { type });
2055
- return { name, blob };
2191
+ get readOnly() {
2192
+ if (this.parent?.readOnly !== undefined) {
2193
+ return this.parent.readOnly ? true : this._jsonModel.readOnly;
2056
2194
  }
2057
2195
  else {
2058
- const blob = new window.Blob([groups[4]], { type });
2059
- return { name, blob };
2196
+ return this._jsonModel.readOnly;
2060
2197
  }
2061
2198
  }
2062
- else {
2063
- return null;
2199
+ set readOnly(e) {
2200
+ this._setProperty('readOnly', e, true, this.notifyChildren);
2064
2201
  }
2202
+ notifyChildren(action) {
2203
+ if (action.payload !== undefined && action.payload.changes !== undefined) {
2204
+ for (const change of action.payload.changes) {
2205
+ if (change.propertyName !== undefined && notifyChildrenAttributes.includes(change.propertyName)) {
2206
+ this.items.forEach((child) => {
2207
+ this.notifyDependents.call(child, propertyChange(change.propertyName, child.getState()[change.propertyName], null));
2208
+ if (child.fieldType === 'panel') {
2209
+ this.notifyChildren.call(child, action);
2210
+ }
2211
+ });
2212
+ }
2213
+ }
2214
+ }
2215
+ }
2216
+ }
2217
+ __decorate([
2218
+ dependencyTracked()
2219
+ ], Container.prototype, "maxItems", null);
2220
+ __decorate([
2221
+ dependencyTracked()
2222
+ ], Container.prototype, "minItems", null);
2223
+ __decorate([
2224
+ dependencyTracked()
2225
+ ], Container.prototype, "activeChild", null);
2226
+
2227
+ class Node {
2228
+ _jsonModel;
2229
+ constructor(inputModel) {
2230
+ this._jsonModel = {
2231
+ ...inputModel
2232
+ };
2233
+ }
2234
+ getP(key, def) {
2235
+ return getProperty(this._jsonModel, key, def);
2236
+ }
2237
+ get isContainer() {
2238
+ return false;
2239
+ }
2240
+ }
2241
+
2242
+ class FormMetaData extends Node {
2243
+ get version() {
2244
+ return this.getP('version', '');
2245
+ }
2246
+ get grammar() {
2247
+ return this.getP('grammar', '');
2248
+ }
2249
+ }
2250
+
2251
+ class SubmitMetaData {
2252
+ lang;
2253
+ captchaInfo;
2254
+ constructor(lang = '', captchaInfo) {
2255
+ this.lang = lang;
2256
+ this.captchaInfo = captchaInfo;
2257
+ }
2258
+ }
2259
+
2260
+ const levels = {
2261
+ off: 0,
2262
+ debug: 1,
2263
+ info: 2,
2264
+ warn: 3,
2265
+ error: 4
2065
2266
  };
2066
- const isFormOrSiteContainer = (model) => {
2067
- return (':items' in model || 'cqItems' in model) && (':itemsOrder' in model || 'cqItemsOrder' in model);
2068
- };
2069
- const sitesModelToFormModel = (sitesModel) => {
2070
- if (!sitesModel || !Object.keys(sitesModel).length) {
2071
- return sitesModel;
2267
+ class Logger {
2268
+ debug(msg) {
2269
+ this.log(msg, 'debug');
2072
2270
  }
2073
- if (isFormOrSiteContainer(sitesModel)) {
2074
- const itemsArr = [];
2075
- const itemsOrder = sitesModel[':itemsOrder'] || sitesModel.cqItemsOrder;
2076
- const items = sitesModel[':items'] || sitesModel.cqItems;
2077
- itemsOrder.forEach((elemName) => {
2078
- itemsArr.push(sitesModelToFormModel(items[elemName]));
2271
+ info(msg) {
2272
+ this.log(msg, 'info');
2273
+ }
2274
+ warn(msg) {
2275
+ this.log(msg, 'warn');
2276
+ }
2277
+ error(msg) {
2278
+ this.log(msg, 'error');
2279
+ }
2280
+ log(msg, level) {
2281
+ if (this.logLevel !== 0 && this.logLevel <= levels[level]) {
2282
+ console[level](msg);
2283
+ }
2284
+ }
2285
+ logLevel;
2286
+ constructor(logLevel = 'off') {
2287
+ this.logLevel = levels[logLevel];
2288
+ }
2289
+ }
2290
+
2291
+ class EventNode {
2292
+ _node;
2293
+ _event;
2294
+ constructor(_node, _event) {
2295
+ this._node = _node;
2296
+ this._event = _event;
2297
+ }
2298
+ get node() {
2299
+ return this._node;
2300
+ }
2301
+ get event() {
2302
+ return this._event;
2303
+ }
2304
+ isEqual(that) {
2305
+ return that !== null && that !== undefined && this._node == that._node && this._event.type == that._event.type;
2306
+ }
2307
+ toString() {
2308
+ return this._node.id + '__' + this.event.type;
2309
+ }
2310
+ valueOf() {
2311
+ return this.toString();
2312
+ }
2313
+ }
2314
+ class EventQueue {
2315
+ logger;
2316
+ static MAX_EVENT_CYCLE_COUNT = 10;
2317
+ _runningEventCount;
2318
+ _isProcessing = false;
2319
+ _pendingEvents = [];
2320
+ constructor(logger = new Logger('off')) {
2321
+ this.logger = logger;
2322
+ this._runningEventCount = {};
2323
+ }
2324
+ get length() {
2325
+ return this._pendingEvents.length;
2326
+ }
2327
+ get isProcessing() {
2328
+ return this._isProcessing;
2329
+ }
2330
+ isQueued(node, event) {
2331
+ const evntNode = new EventNode(node, event);
2332
+ return this._pendingEvents.find(x => evntNode.isEqual(x)) !== undefined;
2333
+ }
2334
+ queue(node, events, priority = false) {
2335
+ if (!node || !events) {
2336
+ return;
2337
+ }
2338
+ if (!(events instanceof Array)) {
2339
+ events = [events];
2340
+ }
2341
+ events.forEach(e => {
2342
+ const evntNode = new EventNode(node, e);
2343
+ const counter = this._runningEventCount[evntNode.valueOf()] || 0;
2344
+ if (counter < EventQueue.MAX_EVENT_CYCLE_COUNT) {
2345
+ this.logger.info(`Queued event : ${e.type} node: ${node.id} - ${node.name}`);
2346
+ if (priority) {
2347
+ const index = this._isProcessing ? 1 : 0;
2348
+ this._pendingEvents.splice(index, 0, evntNode);
2349
+ }
2350
+ else {
2351
+ this._pendingEvents.push(evntNode);
2352
+ }
2353
+ this._runningEventCount[evntNode.valueOf()] = counter + 1;
2354
+ }
2355
+ else {
2356
+ this.logger.info(`Skipped queueing event : ${e.type} node: ${node.id} - ${node.name} with count=${counter}`);
2357
+ }
2079
2358
  });
2080
- sitesModel.items = itemsArr;
2081
2359
  }
2082
- return sitesModel;
2083
- };
2084
- const replaceTemplatePlaceholders = (str, values = []) => {
2085
- return str?.replace(/\${(\d+)}/g, (match, index) => {
2086
- const replacement = values[index];
2087
- return typeof replacement !== 'undefined' ? replacement : match;
2088
- });
2089
- };
2360
+ empty() {
2361
+ this._pendingEvents = [];
2362
+ }
2363
+ runPendingQueue() {
2364
+ if (this._isProcessing) {
2365
+ return;
2366
+ }
2367
+ this._isProcessing = true;
2368
+ while (this._pendingEvents.length > 0) {
2369
+ const e = this._pendingEvents[0];
2370
+ this.logger.info(`Dequeued event : ${e.event.type} node: ${e.node.id} - ${e.node.name}`);
2371
+ e.node.executeAction(e.event);
2372
+ this._pendingEvents.shift();
2373
+ }
2374
+ this._runningEventCount = {};
2375
+ this._isProcessing = false;
2376
+ }
2377
+ }
2090
2378
 
2091
2379
  const request$1 = (url, data = null, options = {}) => {
2092
2380
  const opts = { ...defaultRequestOptions, ...options };
@@ -2203,7 +2491,8 @@ const request = async (context, uri, httpVerb, payload, success, error, headers)
2203
2491
  else {
2204
2492
  context.form.logger.error('Error invoking a rest API');
2205
2493
  const eName = getCustomEventName(error);
2206
- if (error === 'submitFailure') {
2494
+ if (error === 'submitError') {
2495
+ context.form.dispatch(new SubmitError(result, true));
2207
2496
  context.form.dispatch(new SubmitFailure(result, true));
2208
2497
  }
2209
2498
  else {
@@ -2334,6 +2623,11 @@ class FunctionRuntimeImpl {
2334
2623
  },
2335
2624
  exportData: () => {
2336
2625
  return FunctionRuntimeImpl.getInstance().getFunctions().exportData._func.call(undefined, args, data, interpreter);
2626
+ },
2627
+ submitForm: (payload, validateForm, contentType) => {
2628
+ const submitAs = contentType || 'multipart/form-data';
2629
+ const args = ['custom:submitSuccess', 'custom:submitError', submitAs, payload, validateForm];
2630
+ return FunctionRuntimeImpl.getInstance().getFunctions().submitForm._func.call(undefined, args, data, interpreter);
2337
2631
  }
2338
2632
  }
2339
2633
  };
@@ -2435,11 +2729,24 @@ class FunctionRuntimeImpl {
2435
2729
  },
2436
2730
  submitForm: {
2437
2731
  _func: (args, data, interpreter) => {
2438
- const success = toString(args[0]);
2439
- const error = toString(args[1]);
2440
- const submit_as = args.length > 2 ? toString(args[2]) : 'multipart/form-data';
2441
- const submit_data = args.length > 3 ? valueOf(args[3]) : null;
2442
- const validate_form = args.length > 4 ? valueOf(args[4]) : true;
2732
+ let success = 'custom:submitSuccess';
2733
+ let error = 'custom:submitError';
2734
+ let submit_data;
2735
+ let validate_form;
2736
+ let submit_as;
2737
+ if (args.length > 0 && typeof valueOf(args[0]) === 'object') {
2738
+ submit_data = args.length > 0 ? valueOf(args[0]) : null;
2739
+ validate_form = args.length > 1 ? valueOf(args[1]) : true;
2740
+ submit_as = args.length > 2 ? toString(args[2]) : 'multipart/form-data';
2741
+ }
2742
+ else {
2743
+ interpreter.globals.form.logger.warn('This usage of submitForm is deprecated. Please see the documentation and update');
2744
+ success = toString(args[0]);
2745
+ error = toString(args[1]);
2746
+ submit_as = args.length > 2 ? toString(args[2]) : 'multipart/form-data';
2747
+ submit_data = args.length > 3 ? valueOf(args[3]) : null;
2748
+ validate_form = args.length > 4 ? valueOf(args[4]) : true;
2749
+ }
2443
2750
  interpreter.globals.form.dispatch(new Submit({
2444
2751
  success,
2445
2752
  error,
@@ -2776,7 +3083,7 @@ class Form extends Container {
2776
3083
  if (!validate_form || this.validate().length === 0) {
2777
3084
  const payload = action?.payload || {};
2778
3085
  const successEventName = payload?.success ? payload?.success : 'submitSuccess';
2779
- const failureEventName = payload?.error ? payload?.error : 'submitFailure';
3086
+ const failureEventName = payload?.error ? payload?.error : 'submitError';
2780
3087
  submit(context, successEventName, failureEventName, payload?.submit_as, payload?.data);
2781
3088
  }
2782
3089
  }
@@ -2852,379 +3159,101 @@ class RuleEngine {
2852
3159
  }
2853
3160
  execute(node, data, globals, useValueOf = false) {
2854
3161
  const { formula, ast } = node;
2855
- const oldContext = this._context;
2856
- this._context = globals;
2857
- let res = undefined;
2858
- try {
2859
- res = formula.run(ast, data, 'en-US', globals);
2860
- }
2861
- catch (err) {
2862
- this._context?.form?.logger?.error(err);
2863
- }
2864
- while (this.debugInfo.length > 0) {
2865
- this._context?.form?.logger?.debug(this.debugInfo.pop());
2866
- }
2867
- let finalRes = res;
2868
- if (useValueOf) {
2869
- if (typeof res === 'object' && res !== null) {
2870
- finalRes = Object.getPrototypeOf(res).valueOf.call(res);
2871
- }
2872
- }
2873
- this._context = oldContext;
2874
- return finalRes;
2875
- }
2876
- trackDependency(subscriber) {
2877
- if (this._context && this._context.field !== undefined && this._context.field !== subscriber) {
2878
- subscriber._addDependent(this._context.field);
2879
- }
2880
- }
2881
- }
2882
-
2883
- class Fieldset extends Container {
2884
- constructor(params, _options) {
2885
- super(params, _options);
2886
- if (_options.mode !== 'restore') {
2887
- this._applyDefaults();
2888
- this.queueEvent(new Initialize());
2889
- this.queueEvent(new ExecuteRule());
2890
- }
2891
- }
2892
- _getDefaults() {
2893
- return {
2894
- ...super._getDefaults(),
2895
- visible: true,
2896
- required: false,
2897
- label: {
2898
- visible: true,
2899
- richText: false
2900
- }
2901
- };
2902
- }
2903
- _applyDefaults() {
2904
- super._applyDefaultsInModel();
2905
- if (this._jsonModel.dataRef && this._jsonModel.type === undefined) {
2906
- this._jsonModel.type = 'object';
2907
- }
2908
- }
2909
- get type() {
2910
- const ret = super.type;
2911
- if (ret === 'array' || ret === 'object') {
2912
- return ret;
2913
- }
2914
- return undefined;
2915
- }
2916
- get items() {
2917
- return super.items;
2918
- }
2919
- get value() {
2920
- return null;
2921
- }
2922
- get fieldType() {
2923
- return 'panel';
2924
- }
2925
- }
2926
-
2927
- class InstanceManager extends Fieldset {
2928
- get maxOccur() {
2929
- return this._jsonModel.maxItems;
2930
- }
2931
- set maxOccur(m) {
2932
- this.maxItems = m;
2933
- }
2934
- get minOccur() {
2935
- return this.minItems;
2936
- }
2937
- addInstance(action) {
2938
- return this.addItem(action);
2939
- }
2940
- removeInstance(action) {
2941
- return this.removeItem(action);
2942
- }
2943
- }
2944
- __decorate([
2945
- dependencyTracked()
2946
- ], InstanceManager.prototype, "maxOccur", null);
2947
- __decorate([
2948
- dependencyTracked()
2949
- ], InstanceManager.prototype, "minOccur", null);
2950
-
2951
- const dateRegex = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
2952
- const emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
2953
- const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
2954
- const daysInMonth = (leapYear, month) => {
2955
- if (leapYear && month == 2) {
2956
- return 29;
2957
- }
2958
- return days[month - 1];
2959
- };
2960
- const isLeapYear = (year) => {
2961
- return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0;
2962
- };
2963
- const coerceType = (param, type) => {
2964
- let num;
2965
- switch (type) {
2966
- case 'string':
2967
- return param + '';
2968
- case 'number':
2969
- num = +param;
2970
- if (!isNaN(num)) {
2971
- return num;
2972
- }
2973
- break;
2974
- case 'boolean':
2975
- if (typeof param === 'string') {
2976
- return param === 'true';
2977
- }
2978
- else if (typeof param === 'number') {
2979
- return param !== 0;
2980
- }
2981
- }
2982
- throw `${param} has invalid type. Expected : ${type}, Actual ${typeof param}`;
2983
- };
2984
- const checkNumber = (inputVal) => {
2985
- if (inputVal === '' || inputVal == null) {
2986
- return {
2987
- value: '', valid: true
2988
- };
2989
- }
2990
- let value = parseFloat(inputVal);
2991
- const valid = !isNaN(value);
2992
- if (!valid) {
2993
- value = inputVal;
2994
- }
2995
- return {
2996
- value, valid
2997
- };
2998
- };
2999
- const checkInteger = (inputVal) => {
3000
- if (inputVal == '' || inputVal == null) {
3001
- return {
3002
- value: '', valid: true
3003
- };
3004
- }
3005
- let value = parseFloat(inputVal);
3006
- const valid = !isNaN(value) && Math.round(value) === value;
3007
- if (!valid) {
3008
- value = inputVal;
3009
- }
3010
- return {
3011
- value, valid
3012
- };
3013
- };
3014
- const toArray = (inputVal) => {
3015
- if (inputVal != null && !(inputVal instanceof Array)) {
3016
- return [inputVal];
3017
- }
3018
- return inputVal;
3019
- };
3020
- const checkBool = (inputVal) => {
3021
- const valid = typeof inputVal === 'boolean' || inputVal === 'true' || inputVal === 'false';
3022
- const value = typeof inputVal === 'boolean' ? inputVal : (valid ? inputVal === 'true' : inputVal);
3023
- return { valid, value };
3024
- };
3025
- const checkFile = (inputVal) => {
3026
- const value = extractFileInfo(inputVal);
3027
- const valid = value !== null;
3028
- return {
3029
- value: valid ? value : inputVal,
3030
- valid
3031
- };
3032
- };
3033
- const matchMediaType = (mediaType, accepts) => {
3034
- return !mediaType || accepts.some((accept) => {
3035
- const trimmedAccept = accept.trim();
3036
- const prefixAccept = trimmedAccept.split('/')[0];
3037
- const suffixAccept = trimmedAccept.split('.')[1];
3038
- return ((trimmedAccept.includes('*') && mediaType.startsWith(prefixAccept)) ||
3039
- (trimmedAccept.includes('.') && mediaType.endsWith(suffixAccept)) ||
3040
- (trimmedAccept === mediaType));
3041
- });
3042
- };
3043
- const partitionArray = (inputVal, validatorFn) => {
3044
- const value = toArray(inputVal);
3045
- if (value == null) {
3046
- return [[], [value]];
3047
- }
3048
- return value.reduce((acc, x) => {
3049
- if (acc[1].length == 0) {
3050
- const r = validatorFn(x);
3051
- const index = r.valid ? 0 : 1;
3052
- acc[index].push(r.value);
3053
- }
3054
- return acc;
3055
- }, [[], []]);
3056
- };
3057
- const ValidConstraints = {
3058
- date: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'format'],
3059
- string: ['minLength', 'maxLength', 'pattern'],
3060
- number: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
3061
- array: ['minItems', 'maxItems', 'uniqueItems'],
3062
- file: ['accept', 'maxFileSize'],
3063
- email: ['minLength', 'maxLength', 'format', 'pattern']
3064
- };
3065
- const Constraints = {
3066
- type: (constraint, inputVal) => {
3067
- let value = inputVal;
3068
- if (inputVal == undefined) {
3069
- return {
3070
- valid: true,
3071
- value: inputVal
3072
- };
3162
+ const oldContext = this._context;
3163
+ this._context = globals;
3164
+ let res = undefined;
3165
+ try {
3166
+ res = formula.run(ast, data, 'en-US', globals);
3073
3167
  }
3074
- let valid = true, res;
3075
- switch (constraint) {
3076
- case 'string':
3077
- valid = true;
3078
- value = inputVal.toString();
3079
- break;
3080
- case 'string[]':
3081
- value = toArray(inputVal);
3082
- break;
3083
- case 'number':
3084
- res = checkNumber(inputVal);
3085
- value = res.value;
3086
- valid = res.valid;
3087
- break;
3088
- case 'boolean':
3089
- res = checkBool(inputVal);
3090
- valid = res.valid;
3091
- value = res.value;
3092
- break;
3093
- case 'integer':
3094
- res = checkInteger(inputVal);
3095
- valid = res.valid;
3096
- value = res.value;
3097
- break;
3098
- case 'integer[]':
3099
- res = partitionArray(inputVal, checkInteger);
3100
- valid = res[1].length === 0;
3101
- value = valid ? res[0] : inputVal;
3102
- break;
3103
- case 'file':
3104
- res = checkFile(inputVal instanceof Array ? inputVal[0] : inputVal);
3105
- valid = res.valid;
3106
- value = res.value;
3107
- break;
3108
- case 'file[]':
3109
- res = partitionArray(inputVal, checkFile);
3110
- valid = res[1].length === 0;
3111
- value = valid ? res[0] : inputVal;
3112
- break;
3113
- case 'number[]':
3114
- res = partitionArray(inputVal, checkNumber);
3115
- valid = res[1].length === 0;
3116
- value = valid ? res[0] : inputVal;
3117
- break;
3118
- case 'boolean[]':
3119
- res = partitionArray(inputVal, checkBool);
3120
- valid = res[1].length === 0;
3121
- value = valid ? res[0] : inputVal;
3122
- break;
3168
+ catch (err) {
3169
+ this._context?.form?.logger?.error(err);
3123
3170
  }
3124
- return {
3125
- valid,
3126
- value
3127
- };
3128
- },
3129
- format: (constraint, input) => {
3130
- let valid = true;
3131
- const value = input;
3132
- if (input === null) {
3133
- return { value, valid };
3171
+ while (this.debugInfo.length > 0) {
3172
+ this._context?.form?.logger?.debug(this.debugInfo.pop());
3134
3173
  }
3135
- let res;
3136
- switch (constraint) {
3137
- case 'date':
3138
- res = dateRegex.exec((input || '').trim());
3139
- if (res != null) {
3140
- const [match, year, month, date] = res;
3141
- const [nMonth, nDate] = [+month, +date];
3142
- const leapYear = isLeapYear(+year);
3143
- valid = (nMonth >= 1 && nMonth <= 12) &&
3144
- (nDate >= 1 && nDate <= daysInMonth(leapYear, nMonth));
3145
- }
3146
- else {
3147
- valid = false;
3148
- }
3149
- break;
3150
- case 'email':
3151
- valid = new RegExp(emailRegex).test((input || '').trim());
3152
- break;
3153
- case 'data-url':
3154
- valid = true;
3155
- break;
3174
+ let finalRes = res;
3175
+ if (useValueOf) {
3176
+ if (typeof res === 'object' && res !== null) {
3177
+ finalRes = Object.getPrototypeOf(res).valueOf.call(res);
3178
+ }
3156
3179
  }
3157
- return { valid, value };
3158
- },
3159
- minimum: (constraint, value) => {
3160
- return { valid: value >= constraint, value };
3161
- },
3162
- maximum: (constraint, value) => {
3163
- return { valid: value <= constraint, value };
3164
- },
3165
- exclusiveMinimum: (constraint, value) => {
3166
- return { valid: value > constraint, value };
3167
- },
3168
- exclusiveMaximum: (constraint, value) => {
3169
- return { valid: value < constraint, value };
3170
- },
3171
- minItems: (constraint, value) => {
3172
- return { valid: (value instanceof Array) && value.length >= constraint, value };
3173
- },
3174
- maxItems: (constraint, value) => {
3175
- return { valid: (value instanceof Array) && value.length <= constraint, value };
3176
- },
3177
- uniqueItems: (constraint, value) => {
3178
- return { valid: !constraint || ((value instanceof Array) && value.length === new Set(value).size), value };
3179
- },
3180
- minLength: (constraint, value) => {
3181
- return { ...Constraints.minimum(constraint, typeof value === 'string' ? value.length : 0), value };
3182
- },
3183
- maxLength: (constraint, value) => {
3184
- return { ...Constraints.maximum(constraint, typeof value === 'string' ? value.length : 0), value };
3185
- },
3186
- pattern: (constraint, value) => {
3187
- let regex;
3188
- if (typeof constraint === 'string') {
3189
- regex = new RegExp(constraint);
3180
+ this._context = oldContext;
3181
+ return finalRes;
3182
+ }
3183
+ trackDependency(subscriber) {
3184
+ if (this._context && this._context.field !== undefined && this._context.field !== subscriber) {
3185
+ subscriber._addDependent(this._context.field);
3190
3186
  }
3191
- else {
3192
- regex = constraint;
3187
+ }
3188
+ }
3189
+
3190
+ class Fieldset extends Container {
3191
+ constructor(params, _options) {
3192
+ super(params, _options);
3193
+ if (_options.mode !== 'restore') {
3194
+ this._applyDefaults();
3195
+ this.queueEvent(new Initialize());
3196
+ this.queueEvent(new ExecuteRule());
3193
3197
  }
3194
- return { valid: regex.test(value), value };
3195
- },
3196
- required: (constraint, value) => {
3197
- const valid = constraint ? value != null && value !== '' : true;
3198
- return { valid, value };
3199
- },
3200
- enum: (constraint, value) => {
3198
+ }
3199
+ _getDefaults() {
3201
3200
  return {
3202
- valid: constraint.indexOf(value) > -1,
3203
- value
3201
+ ...super._getDefaults(),
3202
+ visible: true,
3203
+ required: false,
3204
+ label: {
3205
+ visible: true,
3206
+ richText: false
3207
+ }
3204
3208
  };
3205
- },
3206
- accept: (constraint, value) => {
3207
- if (!constraint || constraint.length === 0 || value === null || value === undefined) {
3208
- return {
3209
- valid: true,
3210
- value
3211
- };
3209
+ }
3210
+ _applyDefaults() {
3211
+ super._applyDefaultsInModel();
3212
+ if (this._jsonModel.dataRef && this._jsonModel.type === undefined) {
3213
+ this._jsonModel.type = 'object';
3212
3214
  }
3213
- const tempValue = value instanceof Array ? value : [value];
3214
- const invalidFile = tempValue.some((file) => !matchMediaType(file.type, constraint));
3215
- return {
3216
- valid: !invalidFile,
3217
- value
3218
- };
3219
- },
3220
- maxFileSize: (constraint, value) => {
3221
- const sizeLimit = typeof constraint === 'string' ? getFileSizeInBytes(constraint) : constraint;
3222
- return {
3223
- valid: !(value instanceof FileObject) || value.size <= sizeLimit,
3224
- value
3225
- };
3226
3215
  }
3227
- };
3216
+ get type() {
3217
+ const ret = super.type;
3218
+ if (ret === 'array' || ret === 'object') {
3219
+ return ret;
3220
+ }
3221
+ return undefined;
3222
+ }
3223
+ get items() {
3224
+ return super.items;
3225
+ }
3226
+ get value() {
3227
+ return null;
3228
+ }
3229
+ get fieldType() {
3230
+ return 'panel';
3231
+ }
3232
+ }
3233
+
3234
+ class InstanceManager extends Fieldset {
3235
+ get maxOccur() {
3236
+ return this._jsonModel.maxItems;
3237
+ }
3238
+ set maxOccur(m) {
3239
+ this.maxItems = m;
3240
+ }
3241
+ get minOccur() {
3242
+ return this.minItems;
3243
+ }
3244
+ addInstance(action) {
3245
+ return this.addItem(action);
3246
+ }
3247
+ removeInstance(action) {
3248
+ return this.removeItem(action);
3249
+ }
3250
+ }
3251
+ __decorate([
3252
+ dependencyTracked()
3253
+ ], InstanceManager.prototype, "maxOccur", null);
3254
+ __decorate([
3255
+ dependencyTracked()
3256
+ ], InstanceManager.prototype, "minOccur", null);
3228
3257
 
3229
3258
  const validTypes = ['string', 'number', 'integer', 'boolean', 'file', 'string[]', 'number[]', 'integer[]', 'boolean[]', 'file[]', 'array', 'object'];
3230
3259
  class Field extends Scriptable {
@@ -3449,7 +3478,6 @@ class Field extends Scriptable {
3449
3478
  }
3450
3479
  set required(r) {
3451
3480
  this._setProperty('required', r);
3452
- this.validate();
3453
3481
  }
3454
3482
  get maximum() {
3455
3483
  if (this.type === 'number' || this.format === 'date' || this.type === 'integer') {
@@ -4078,6 +4106,17 @@ class Checkbox extends Field {
4078
4106
  baseConstraints.required = requiredConstraint(this.offValue());
4079
4107
  return baseConstraints;
4080
4108
  }
4109
+ _applyDefaults() {
4110
+ if (typeof this._jsonModel.checked === 'boolean') {
4111
+ if (this._jsonModel.checked) {
4112
+ this._jsonModel.default = this._jsonModel.enum?.[0];
4113
+ }
4114
+ else {
4115
+ this._jsonModel.default = this._jsonModel.enum?.[1];
4116
+ }
4117
+ }
4118
+ super._applyDefaults();
4119
+ }
4081
4120
  _getDefaults() {
4082
4121
  return {
4083
4122
  ...super._getDefaults(),
@@ -4087,7 +4126,43 @@ class Checkbox extends Field {
4087
4126
  get enum() {
4088
4127
  return this._jsonModel.enum || [];
4089
4128
  }
4129
+ updateDataNodeAndTypedValue(val) {
4130
+ const changes = super.updateDataNodeAndTypedValue(val);
4131
+ const valueChange = changes.find((c) => c.propertyName === 'value');
4132
+ if (valueChange) {
4133
+ const oldChecked = valueChange.prevValue === this._jsonModel.enum?.[0];
4134
+ const newChecked = valueChange.currentValue === this._jsonModel.enum?.[0];
4135
+ if (oldChecked !== newChecked) {
4136
+ changes.push({
4137
+ propertyName: 'checked',
4138
+ prevValue: oldChecked,
4139
+ currentValue: newChecked
4140
+ });
4141
+ }
4142
+ }
4143
+ return changes;
4144
+ }
4145
+ set checked(check) {
4146
+ if (check) {
4147
+ this.value = this._jsonModel.enum?.[0];
4148
+ }
4149
+ else {
4150
+ this.value = this._jsonModel.enum?.[1];
4151
+ }
4152
+ }
4153
+ get checked() {
4154
+ return this.value === this._jsonModel.enum?.[0];
4155
+ }
4156
+ getState(isRepeatableChild = false, forRestore = false) {
4157
+ return {
4158
+ ...super.getState(isRepeatableChild, forRestore),
4159
+ checked: this.checked
4160
+ };
4161
+ }
4090
4162
  }
4163
+ __decorate([
4164
+ dependencyTracked()
4165
+ ], Checkbox.prototype, "checked", null);
4091
4166
 
4092
4167
  class CheckboxGroup extends Field {
4093
4168
  constructor(params, _options) {
@@ -4259,9 +4334,13 @@ const createFormInstance = (formModel, callback, logLevel = 'error', fModel = un
4259
4334
  const defaultOptions = {
4260
4335
  logLevel: 'error'
4261
4336
  };
4262
- const restoreFormInstance = (formModel, { logLevel } = defaultOptions) => {
4337
+ const restoreFormInstance = (formModel, data = null, { logLevel } = defaultOptions) => {
4263
4338
  try {
4264
4339
  const form = new Form({ ...formModel }, FormFieldFactory, new RuleEngine(), new EventQueue(new Logger(logLevel)), logLevel, 'restore');
4340
+ if (data) {
4341
+ form._bindToDataModel(new DataGroup('$form', data));
4342
+ form.syncDataAndFormModel(form.getDataNode());
4343
+ }
4265
4344
  form.getEventQueue().empty();
4266
4345
  return form;
4267
4346
  }