@aemforms/af-core 0.22.74 → 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.
@@ -578,285 +578,812 @@ 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
- 'checked'
601
- ];
602
- const dynamicProps = [
603
- ...editableProperties,
604
- 'index',
605
- 'activeChild'
606
- ];
607
- const staticFields = ['plain-text', 'image'];
608
- class ActionImplWithTarget {
609
- _action;
610
- _target;
611
- constructor(_action, _target) {
612
- this._action = _action;
613
- 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);
614
588
  }
615
589
  get type() {
616
- return this._action.type;
617
- }
618
- get payload() {
619
- return this._action.payload;
620
- }
621
- get metadata() {
622
- return this._action.metadata;
623
- }
624
- get target() {
625
- return this._target;
626
- }
627
- get isCustomEvent() {
628
- return this._action.isCustomEvent;
590
+ return this.mediaType;
629
591
  }
630
- get originalAction() {
631
- 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
+ };
632
599
  }
633
- toString() {
634
- 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);
635
605
  }
636
606
  }
637
- const target = Symbol('target');
638
- const qualifiedName = Symbol('qualifiedName');
639
- function dependencyTracked() {
640
- return function (target, propertyKey, descriptor) {
641
- const get = descriptor.get;
642
- if (get != undefined) {
643
- descriptor.get = function () {
644
- this.ruleEngine.trackDependency(this);
645
- return get.call(this);
646
- };
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));
647
616
  }
648
- };
649
- }
650
- const addOnly = (includeOrExclude) => (...fieldTypes) => (target, propertyKey, descriptor) => {
651
- const get = descriptor.get;
652
- if (get != undefined) {
653
- descriptor.get = function () {
654
- if (fieldTypes.indexOf(this.fieldType) > -1 === includeOrExclude) {
655
- return get.call(this);
656
- }
657
- return undefined;
658
- };
659
- }
660
- const set = descriptor.set;
661
- if (set != undefined) {
662
- descriptor.set = function (value) {
663
- if (fieldTypes.indexOf(this.fieldType) > -1 === includeOrExclude) {
664
- set.call(this, value);
665
- }
666
- };
617
+ else {
618
+ randIndex = Math.floor(Math.random() * (chars.length));
619
+ }
620
+ ret.push(chars[randIndex]);
667
621
  }
622
+ return ret.join('');
668
623
  };
669
- const include = addOnly(true);
670
- const exclude = addOnly(false);
671
- class BaseNode {
672
- _options;
673
- _ruleNode;
674
- _lang = '';
675
- _callbacks = {};
676
- _dependents = [];
677
- _jsonModel;
678
- _tokens = [];
679
- get isContainer() {
680
- return false;
681
- }
682
- constructor(params, _options) {
683
- this._options = _options;
684
- this[qualifiedName] = null;
685
- this._jsonModel = {
686
- ...params,
687
- id: 'id' in params ? params.id : this.form.getUniqueId()
688
- };
689
- }
690
- setupRuleNode() {
691
- const self = this;
692
- this._ruleNode = new Proxy(this.ruleNodeReference(), {
693
- get: (ruleNodeReference, prop) => {
694
- return self.getFromRule(ruleNodeReference, prop);
695
- }
696
- });
697
- }
698
- ruleNodeReference() {
699
- return this;
700
- }
701
- getRuleNode() {
702
- return this._ruleNode;
703
- }
704
- getFromRule(ruleNodeReference, prop) {
705
- if (prop === Symbol.toPrimitive || (prop === 'valueOf' && !ruleNodeReference.hasOwnProperty('valueOf'))) {
706
- 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;
707
629
  }
708
- else if (prop === target) {
709
- return this;
630
+ let ret = null;
631
+ if (item.isContainer) {
632
+ ret = getAttachments(item, excludeUnbound);
710
633
  }
711
- else if (typeof (prop) === 'string') {
712
- if (prop.startsWith('$')) {
713
- prop = prop.substr(1);
714
- if (typeof this[prop] !== 'function') {
715
- const retValue = this[prop];
716
- if (retValue instanceof BaseNode) {
717
- return retValue.getRuleNode();
718
- }
719
- else if (retValue instanceof Array) {
720
- return retValue.map(r => r instanceof BaseNode ? r.getRuleNode() : r);
721
- }
722
- else {
723
- return retValue;
724
- }
725
- }
726
- }
727
- else {
728
- if (ruleNodeReference.hasOwnProperty(prop)) {
729
- 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
+ });
730
645
  }
731
- else if (typeof ruleNodeReference[prop] === 'function') {
732
- return ruleNodeReference[prop];
646
+ else if (item.value != null) {
647
+ ret[item.id] = { ...item.value, 'dataRef': dataRef };
733
648
  }
734
649
  }
735
650
  }
736
- }
737
- get id() {
738
- return this._jsonModel.id;
739
- }
740
- get index() {
741
- if (this.parent) {
742
- 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());
743
660
  }
744
- return 0;
745
- }
746
- get parent() {
747
- return this._options.parent;
748
- }
749
- get type() {
750
- return this._jsonModel.type;
751
- }
752
- get repeatable() {
753
- return this.parent?.hasDynamicItems();
754
- }
755
- get fieldType() {
756
- return this._jsonModel.fieldType || 'text-input';
757
661
  }
758
- get ':type'() {
759
- return this._jsonModel[':type'] || this.fieldType;
760
- }
761
- get name() {
762
- return this._jsonModel.name;
763
- }
764
- get description() {
765
- return this._jsonModel.description;
766
- }
767
- set description(d) {
768
- this._setProperty('description', d);
769
- }
770
- get dataRef() {
771
- return this._jsonModel.dataRef;
772
- }
773
- get visible() {
774
- if (this.parent?.visible !== undefined) {
775
- return this.parent?.visible ? this._jsonModel.visible : false;
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));
776
674
  }
777
- else {
778
- return this._jsonModel.visible;
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();
779
686
  }
780
- }
781
- set visible(v) {
782
- if (v !== this._jsonModel.visible) {
783
- const changeAction = propertyChange('visible', v, this._jsonModel.visible);
784
- this._jsonModel.visible = v;
785
- this.notifyDependents(changeAction);
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);
786
767
  }
768
+ return null;
787
769
  }
788
- get form() {
789
- return this._options.form;
770
+ else {
771
+ return null;
790
772
  }
791
- get ruleEngine() {
792
- 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
+ }
793
794
  }
794
- get label() {
795
- return this._jsonModel.label;
795
+ else {
796
+ return null;
796
797
  }
797
- set label(l) {
798
- if (l !== this._jsonModel.label) {
799
- const changeAction = propertyChange('label', l, this._jsonModel.label);
800
- this._jsonModel = {
801
- ...this._jsonModel,
802
- label: l
803
- };
804
- this.notifyDependents(changeAction);
805
- }
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;
806
805
  }
807
- get uniqueItems() {
808
- 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;
809
814
  }
810
- isTransparent() {
811
- const isNonTransparent = this.parent?._jsonModel.type === 'array';
812
- 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;
813
830
  }
814
- 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) {
815
859
  return {
816
- ...this._jsonModel,
817
- properties: this.properties,
818
- index: this.index,
819
- parent: undefined,
820
- qualifiedName: this.qualifiedName,
821
- repeatable: this.repeatable === true ? true : undefined,
822
- ':type': this[':type'],
823
- ...(forRestore ? {
824
- _dependents: this._dependents.length ? this._dependents.map(x => x.node.id) : undefined,
825
- allowedComponents: undefined,
826
- columnClassNames: undefined,
827
- columnCount: undefined,
828
- gridClassNames: undefined
829
- } : {})
860
+ value: '', valid: true
830
861
  };
831
862
  }
832
- subscribe(callback, eventName = 'change') {
833
- this._callbacks[eventName] = this._callbacks[eventName] || [];
834
- 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) {
835
874
  return {
836
- unsubscribe: () => {
837
- this._callbacks[eventName] = this._callbacks[eventName].filter(x => x !== callback);
838
- }
875
+ value: '', valid: true
839
876
  };
840
877
  }
841
- _addDependent(dependent) {
842
- if (this._dependents.find(({ node }) => node === dependent) === undefined) {
843
- const subscription = this.subscribe((change) => {
844
- const changes = change.payload.changes;
845
- const propsToLook = [...dynamicProps, 'items'];
846
- const isPropChanged = changes.findIndex(x => {
847
- return propsToLook.indexOf(x.propertyName) > -1;
848
- }) > -1;
849
- if (isPropChanged) {
850
- dependent.dispatch(new ExecuteRule());
851
- }
852
- });
853
- this._dependents.push({ node: dependent, subscription });
854
- }
878
+ let value = parseFloat(inputVal);
879
+ const valid = !isNaN(value) && Math.round(value) === value;
880
+ if (!valid) {
881
+ value = inputVal;
855
882
  }
856
- removeDependent(dependent) {
857
- const index = this._dependents.findIndex(({ node }) => node === dependent);
858
- if (index > -1) {
859
- this._dependents[index].subscription.unsubscribe();
883
+ return {
884
+ value, valid
885
+ };
886
+ };
887
+ const toArray = (inputVal) => {
888
+ if (inputVal != null && !(inputVal instanceof Array)) {
889
+ return [inputVal];
890
+ }
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]];
920
+ }
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);
926
+ }
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
+ };
948
+ }
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;
998
+ }
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 };
1009
+ }
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));
1020
+ }
1021
+ else {
1022
+ valid = false;
1023
+ }
1024
+ break;
1025
+ case 'email':
1026
+ valid = new RegExp(emailRegex).test((input || '').trim());
1027
+ break;
1028
+ case 'data-url':
1029
+ valid = true;
1030
+ break;
1031
+ }
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);
1065
+ }
1066
+ else {
1067
+ regex = constraint;
1068
+ }
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
+ };
1087
+ }
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
+ };
1101
+ }
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;
1137
+ }
1138
+ get type() {
1139
+ return this._action.type;
1140
+ }
1141
+ get payload() {
1142
+ return this._action.payload;
1143
+ }
1144
+ get metadata() {
1145
+ return this._action.metadata;
1146
+ }
1147
+ get target() {
1148
+ return this._target;
1149
+ }
1150
+ get isCustomEvent() {
1151
+ return this._action.isCustomEvent;
1152
+ }
1153
+ get originalAction() {
1154
+ return this._action.originalAction;
1155
+ }
1156
+ toString() {
1157
+ return this._action.toString();
1158
+ }
1159
+ }
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
+ };
1170
+ }
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);
1179
+ }
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);
1188
+ }
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;
1204
+ }
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);
1218
+ }
1219
+ });
1220
+ }
1221
+ ruleNodeReference() {
1222
+ return this;
1223
+ }
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];
1239
+ if (retValue instanceof BaseNode) {
1240
+ return retValue.getRuleNode();
1241
+ }
1242
+ else if (retValue instanceof Array) {
1243
+ return retValue.map(r => r instanceof BaseNode ? r.getRuleNode() : r);
1244
+ }
1245
+ else {
1246
+ return retValue;
1247
+ }
1248
+ }
1249
+ }
1250
+ else {
1251
+ if (ruleNodeReference.hasOwnProperty(prop)) {
1252
+ return ruleNodeReference[prop];
1253
+ }
1254
+ else if (typeof ruleNodeReference[prop] === 'function') {
1255
+ return ruleNodeReference[prop];
1256
+ }
1257
+ }
1258
+ }
1259
+ }
1260
+ get id() {
1261
+ return this._jsonModel.id;
1262
+ }
1263
+ get index() {
1264
+ if (this.parent) {
1265
+ return this.parent.indexOf(this);
1266
+ }
1267
+ return 0;
1268
+ }
1269
+ get parent() {
1270
+ return this._options.parent;
1271
+ }
1272
+ get type() {
1273
+ return this._jsonModel.type;
1274
+ }
1275
+ get repeatable() {
1276
+ return this.parent?.hasDynamicItems();
1277
+ }
1278
+ get fieldType() {
1279
+ return this._jsonModel.fieldType || 'text-input';
1280
+ }
1281
+ get ':type'() {
1282
+ return this._jsonModel[':type'] || this.fieldType;
1283
+ }
1284
+ get name() {
1285
+ return this._jsonModel.name;
1286
+ }
1287
+ get description() {
1288
+ return this._jsonModel.description;
1289
+ }
1290
+ set description(d) {
1291
+ this._setProperty('description', d);
1292
+ }
1293
+ get dataRef() {
1294
+ return this._jsonModel.dataRef;
1295
+ }
1296
+ get visible() {
1297
+ if (this.parent?.visible !== undefined) {
1298
+ return this.parent?.visible ? this._jsonModel.visible : false;
1299
+ }
1300
+ else {
1301
+ return this._jsonModel.visible;
1302
+ }
1303
+ }
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) {
1338
+ return {
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
1348
+ } : {}),
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
+ } : {})
1357
+ };
1358
+ }
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);
1365
+ }
1366
+ };
1367
+ }
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
+ }
1379
+ });
1380
+ this._dependents.push({ node: dependent, subscription });
1381
+ }
1382
+ }
1383
+ removeDependent(dependent) {
1384
+ const index = this._dependents.findIndex(({ node }) => node === dependent);
1385
+ if (index > -1) {
1386
+ this._dependents[index].subscription.unsubscribe();
860
1387
  this._dependents.splice(index, 1);
861
1388
  }
862
1389
  }
@@ -904,6 +1431,9 @@ class BaseNode {
904
1431
  this.notifyDependents(changeAction);
905
1432
  }
906
1433
  notifyChildren.call(this, changeAction);
1434
+ if (validationConstraintsList.includes(prop)) {
1435
+ this.validate();
1436
+ }
907
1437
  return changeAction.payload.changes;
908
1438
  }
909
1439
  return [];
@@ -1837,257 +2367,14 @@ class EventQueue {
1837
2367
  this._isProcessing = true;
1838
2368
  while (this._pendingEvents.length > 0) {
1839
2369
  const e = this._pendingEvents[0];
1840
- this.logger.info(`Dequeued event : ${e.event.type} node: ${e.node.id} - ${e.node.name}`);
1841
- e.node.executeAction(e.event);
1842
- this._pendingEvents.shift();
1843
- }
1844
- this._runningEventCount = {};
1845
- this._isProcessing = false;
1846
- }
1847
- }
1848
-
1849
- class FileObject {
1850
- data;
1851
- mediaType = 'application/octet-stream';
1852
- name = 'unknown';
1853
- size = 0;
1854
- constructor(init) {
1855
- Object.assign(this, init);
1856
- }
1857
- get type() {
1858
- return this.mediaType;
1859
- }
1860
- toJSON() {
1861
- return {
1862
- 'name': this.name,
1863
- 'size': this.size,
1864
- 'mediaType': this.mediaType,
1865
- 'data': this.data.toString()
1866
- };
1867
- }
1868
- equals(obj) {
1869
- return (this.data === obj.data &&
1870
- this.mediaType === obj.mediaType &&
1871
- this.name === obj.name &&
1872
- this.size === obj.size);
1873
- }
1874
- }
1875
-
1876
- const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'.split('');
1877
- const fileSizeRegex = /^(\d*\.?\d+)(\\?(?=[KMGT])([KMGT])(?:i?B)?|B?)$/i;
1878
- const randomWord = (l) => {
1879
- const ret = [];
1880
- for (let i = 0; i <= l; i++) {
1881
- let randIndex;
1882
- if (i === 0) {
1883
- randIndex = Math.floor(Math.random() * (chars.length - 11));
1884
- }
1885
- else {
1886
- randIndex = Math.floor(Math.random() * (chars.length));
1887
- }
1888
- ret.push(chars[randIndex]);
1889
- }
1890
- return ret.join('');
1891
- };
1892
- const getAttachments = (input, excludeUnbound = false) => {
1893
- const items = input.items || [];
1894
- return items?.reduce((acc, item) => {
1895
- if (excludeUnbound && item.dataRef === null) {
1896
- return acc;
1897
- }
1898
- let ret = null;
1899
- if (item.isContainer) {
1900
- ret = getAttachments(item, excludeUnbound);
1901
- }
1902
- else {
1903
- if (isFile(item.getState())) {
1904
- ret = {};
1905
- const name = item.name || '';
1906
- const dataRef = (item.dataRef != null)
1907
- ? item.dataRef
1908
- : (name.length > 0 ? item.name : undefined);
1909
- if (item.value instanceof Array) {
1910
- ret[item.id] = item.value.map((x) => {
1911
- return { ...x, 'dataRef': dataRef };
1912
- });
1913
- }
1914
- else if (item.value != null) {
1915
- ret[item.id] = { ...item.value, 'dataRef': dataRef };
1916
- }
1917
- }
1918
- }
1919
- return Object.assign(acc, ret);
1920
- }, {});
1921
- };
1922
- const getFileSizeInBytes = (str) => {
1923
- let retVal = 0;
1924
- if (typeof str === 'string') {
1925
- const matches = fileSizeRegex.exec(str.trim());
1926
- if (matches != null) {
1927
- retVal = sizeToBytes(parseFloat(matches[1]), (matches[2] || 'kb').toUpperCase());
1928
- }
1929
- }
1930
- return retVal;
1931
- };
1932
- const sizeToBytes = (size, symbol) => {
1933
- const sizes = { 'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4 };
1934
- const i = Math.pow(1024, sizes[symbol]);
1935
- return Math.round(size * i);
1936
- };
1937
- const IdGenerator = function* (initial = 50) {
1938
- const initialize = function () {
1939
- const arr = [];
1940
- for (let i = 0; i < initial; i++) {
1941
- arr.push(randomWord(10));
1942
- }
1943
- return arr;
1944
- };
1945
- const passedIds = {};
1946
- let ids = initialize();
1947
- do {
1948
- let x = ids.pop();
1949
- while (x in passedIds) {
1950
- if (ids.length === 0) {
1951
- ids = initialize();
1952
- }
1953
- x = ids.pop();
1954
- }
1955
- passedIds[x] = true;
1956
- yield ids.pop();
1957
- if (ids.length === 0) {
1958
- ids = initialize();
1959
- }
1960
- } while (ids.length > 0);
1961
- };
1962
- const isDataUrl = (str) => {
1963
- const dataUrlRegex = /^data:([a-z]+\/[a-z0-9-+.]+)?;(?:name=(.*);)?base64,(.*)$/;
1964
- return dataUrlRegex.exec(str.trim()) != null;
1965
- };
1966
- const extractFileInfo = (file) => {
1967
- if (file !== null) {
1968
- let retVal = null;
1969
- if (file instanceof FileObject) {
1970
- retVal = file;
1971
- }
1972
- else if (typeof File !== 'undefined' && file instanceof File) {
1973
- retVal = {
1974
- name: file.name,
1975
- mediaType: file.type,
1976
- size: file.size,
1977
- data: file
1978
- };
1979
- }
1980
- else if (typeof file === 'string' && isDataUrl(file)) {
1981
- const result = dataURItoBlob(file);
1982
- if (result !== null) {
1983
- const { blob, name } = result;
1984
- retVal = {
1985
- name: name,
1986
- mediaType: blob.type,
1987
- size: blob.size,
1988
- data: blob
1989
- };
1990
- }
1991
- }
1992
- else {
1993
- let jFile = file;
1994
- try {
1995
- jFile = JSON.parse(file);
1996
- retVal = jFile;
1997
- if (!retVal.mediaType) {
1998
- retVal.mediaType = retVal.type;
1999
- }
2000
- }
2001
- catch (ex) {
2002
- }
2003
- if (typeof jFile?.data === 'string' && isDataUrl(jFile?.data)) {
2004
- const result = dataURItoBlob(jFile?.data);
2005
- if (result !== null) {
2006
- const blob = result.blob;
2007
- retVal = {
2008
- name: jFile?.name,
2009
- mediaType: jFile?.type || jFile?.mediaType,
2010
- size: blob.size,
2011
- data: blob
2012
- };
2013
- }
2014
- }
2015
- else if (typeof jFile === 'string') {
2016
- const fileName = jFile.split('/').pop();
2017
- retVal = {
2018
- name: fileName,
2019
- mediaType: 'application/octet-stream',
2020
- size: 0,
2021
- data: jFile
2022
- };
2023
- }
2024
- else if (typeof jFile === 'object') {
2025
- retVal = {
2026
- name: jFile?.name,
2027
- mediaType: jFile?.type || jFile?.mediaType,
2028
- size: jFile?.size,
2029
- data: jFile?.data
2030
- };
2031
- }
2032
- }
2033
- if (retVal !== null && retVal.data != null) {
2034
- return new FileObject(retVal);
2035
- }
2036
- return null;
2037
- }
2038
- else {
2039
- return null;
2040
- }
2041
- };
2042
- const dataURItoBlob = (dataURI) => {
2043
- const regex = /^data:([a-z]+\/[a-z0-9-+.]+)?(?:;name=([^;]+))?(;base64)?,(.+)$/;
2044
- const groups = regex.exec(dataURI);
2045
- if (groups !== null) {
2046
- const type = groups[1] || '';
2047
- const name = groups[2] || 'unknown';
2048
- const isBase64 = typeof groups[3] === 'string';
2049
- if (isBase64) {
2050
- const binary = atob(groups[4]);
2051
- const array = [];
2052
- for (let i = 0; i < binary.length; i++) {
2053
- array.push(binary.charCodeAt(i));
2054
- }
2055
- const blob = new window.Blob([new Uint8Array(array)], { type });
2056
- return { name, blob };
2057
- }
2058
- else {
2059
- const blob = new window.Blob([groups[4]], { type });
2060
- return { name, blob };
2061
- }
2062
- }
2063
- else {
2064
- return null;
2065
- }
2066
- };
2067
- const isFormOrSiteContainer = (model) => {
2068
- return (':items' in model || 'cqItems' in model) && (':itemsOrder' in model || 'cqItemsOrder' in model);
2069
- };
2070
- const sitesModelToFormModel = (sitesModel) => {
2071
- if (!sitesModel || !Object.keys(sitesModel).length) {
2072
- return sitesModel;
2073
- }
2074
- if (isFormOrSiteContainer(sitesModel)) {
2075
- const itemsArr = [];
2076
- const itemsOrder = sitesModel[':itemsOrder'] || sitesModel.cqItemsOrder;
2077
- const items = sitesModel[':items'] || sitesModel.cqItems;
2078
- itemsOrder.forEach((elemName) => {
2079
- itemsArr.push(sitesModelToFormModel(items[elemName]));
2080
- });
2081
- sitesModel.items = itemsArr;
2082
- }
2083
- return sitesModel;
2084
- };
2085
- const replaceTemplatePlaceholders = (str, values = []) => {
2086
- return str?.replace(/\${(\d+)}/g, (match, index) => {
2087
- const replacement = values[index];
2088
- return typeof replacement !== 'undefined' ? replacement : match;
2089
- });
2090
- };
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
+ }
2091
2378
 
2092
2379
  const request$1 = (url, data = null, options = {}) => {
2093
2380
  const opts = { ...defaultRequestOptions, ...options };
@@ -2336,6 +2623,11 @@ class FunctionRuntimeImpl {
2336
2623
  },
2337
2624
  exportData: () => {
2338
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);
2339
2631
  }
2340
2632
  }
2341
2633
  };
@@ -2437,11 +2729,24 @@ class FunctionRuntimeImpl {
2437
2729
  },
2438
2730
  submitForm: {
2439
2731
  _func: (args, data, interpreter) => {
2440
- const success = toString(args[0]);
2441
- const error = toString(args[1]);
2442
- const submit_as = args.length > 2 ? toString(args[2]) : 'multipart/form-data';
2443
- const submit_data = args.length > 3 ? valueOf(args[3]) : null;
2444
- 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
+ }
2445
2750
  interpreter.globals.form.dispatch(new Submit({
2446
2751
  success,
2447
2752
  error,
@@ -2828,405 +3133,127 @@ function stringToNumber(str, language) {
2828
3133
  }
2829
3134
  return 0;
2830
3135
  }
2831
-
2832
- function getStringToNumberFn(locale) {
2833
- if (locale == null) {
2834
- const localeOptions = new Intl.DateTimeFormat().resolvedOptions();
2835
- locale = localeOptions.locale;
2836
- }
2837
- return (str) => stringToNumber(str, locale);
2838
- }
2839
- class RuleEngine {
2840
- _context;
2841
- _globalNames = [
2842
- '$form',
2843
- '$field',
2844
- '$event'
2845
- ];
2846
- customFunctions;
2847
- debugInfo = [];
2848
- constructor() {
2849
- this.customFunctions = FunctionRuntime.getFunctions();
2850
- }
2851
- compileRule(rule, locale) {
2852
- const formula = new Formula(this.customFunctions, getStringToNumberFn(locale), this.debugInfo);
2853
- return { formula, ast: formula.compile(rule, this._globalNames) };
2854
- }
2855
- execute(node, data, globals, useValueOf = false) {
2856
- const { formula, ast } = node;
2857
- const oldContext = this._context;
2858
- this._context = globals;
2859
- let res = undefined;
2860
- try {
2861
- res = formula.run(ast, data, 'en-US', globals);
2862
- }
2863
- catch (err) {
2864
- this._context?.form?.logger?.error(err);
2865
- }
2866
- while (this.debugInfo.length > 0) {
2867
- this._context?.form?.logger?.debug(this.debugInfo.pop());
2868
- }
2869
- let finalRes = res;
2870
- if (useValueOf) {
2871
- if (typeof res === 'object' && res !== null) {
2872
- finalRes = Object.getPrototypeOf(res).valueOf.call(res);
2873
- }
2874
- }
2875
- this._context = oldContext;
2876
- return finalRes;
2877
- }
2878
- trackDependency(subscriber) {
2879
- if (this._context && this._context.field !== undefined && this._context.field !== subscriber) {
2880
- subscriber._addDependent(this._context.field);
2881
- }
2882
- }
2883
- }
2884
-
2885
- class Fieldset extends Container {
2886
- constructor(params, _options) {
2887
- super(params, _options);
2888
- if (_options.mode !== 'restore') {
2889
- this._applyDefaults();
2890
- this.queueEvent(new Initialize());
2891
- this.queueEvent(new ExecuteRule());
2892
- }
2893
- }
2894
- _getDefaults() {
2895
- return {
2896
- ...super._getDefaults(),
2897
- visible: true,
2898
- required: false,
2899
- label: {
2900
- visible: true,
2901
- richText: false
2902
- }
2903
- };
2904
- }
2905
- _applyDefaults() {
2906
- super._applyDefaultsInModel();
2907
- if (this._jsonModel.dataRef && this._jsonModel.type === undefined) {
2908
- this._jsonModel.type = 'object';
2909
- }
2910
- }
2911
- get type() {
2912
- const ret = super.type;
2913
- if (ret === 'array' || ret === 'object') {
2914
- return ret;
2915
- }
2916
- return undefined;
2917
- }
2918
- get items() {
2919
- return super.items;
2920
- }
2921
- get value() {
2922
- return null;
2923
- }
2924
- get fieldType() {
2925
- return 'panel';
2926
- }
2927
- }
2928
-
2929
- class InstanceManager extends Fieldset {
2930
- get maxOccur() {
2931
- return this._jsonModel.maxItems;
2932
- }
2933
- set maxOccur(m) {
2934
- this.maxItems = m;
2935
- }
2936
- get minOccur() {
2937
- return this.minItems;
2938
- }
2939
- addInstance(action) {
2940
- return this.addItem(action);
2941
- }
2942
- removeInstance(action) {
2943
- return this.removeItem(action);
2944
- }
2945
- }
2946
- __decorate([
2947
- dependencyTracked()
2948
- ], InstanceManager.prototype, "maxOccur", null);
2949
- __decorate([
2950
- dependencyTracked()
2951
- ], InstanceManager.prototype, "minOccur", null);
2952
-
2953
- const dateRegex = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
2954
- const emailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
2955
- const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
2956
- const daysInMonth = (leapYear, month) => {
2957
- if (leapYear && month == 2) {
2958
- return 29;
2959
- }
2960
- return days[month - 1];
2961
- };
2962
- const isLeapYear = (year) => {
2963
- return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0;
2964
- };
2965
- const coerceType = (param, type) => {
2966
- let num;
2967
- switch (type) {
2968
- case 'string':
2969
- return param + '';
2970
- case 'number':
2971
- num = +param;
2972
- if (!isNaN(num)) {
2973
- return num;
2974
- }
2975
- break;
2976
- case 'boolean':
2977
- if (typeof param === 'string') {
2978
- return param === 'true';
2979
- }
2980
- else if (typeof param === 'number') {
2981
- return param !== 0;
2982
- }
2983
- }
2984
- throw `${param} has invalid type. Expected : ${type}, Actual ${typeof param}`;
2985
- };
2986
- const checkNumber = (inputVal) => {
2987
- if (inputVal === '' || inputVal == null) {
2988
- return {
2989
- value: '', valid: true
2990
- };
2991
- }
2992
- let value = parseFloat(inputVal);
2993
- const valid = !isNaN(value);
2994
- if (!valid) {
2995
- value = inputVal;
2996
- }
2997
- return {
2998
- value, valid
2999
- };
3000
- };
3001
- const checkInteger = (inputVal) => {
3002
- if (inputVal == '' || inputVal == null) {
3003
- return {
3004
- value: '', valid: true
3005
- };
3006
- }
3007
- let value = parseFloat(inputVal);
3008
- const valid = !isNaN(value) && Math.round(value) === value;
3009
- if (!valid) {
3010
- value = inputVal;
3011
- }
3012
- return {
3013
- value, valid
3014
- };
3015
- };
3016
- const toArray = (inputVal) => {
3017
- if (inputVal != null && !(inputVal instanceof Array)) {
3018
- return [inputVal];
3019
- }
3020
- return inputVal;
3021
- };
3022
- const checkBool = (inputVal) => {
3023
- const valid = typeof inputVal === 'boolean' || inputVal === 'true' || inputVal === 'false';
3024
- const value = typeof inputVal === 'boolean' ? inputVal : (valid ? inputVal === 'true' : inputVal);
3025
- return { valid, value };
3026
- };
3027
- const checkFile = (inputVal) => {
3028
- const value = extractFileInfo(inputVal);
3029
- const valid = value !== null;
3030
- return {
3031
- value: valid ? value : inputVal,
3032
- valid
3033
- };
3034
- };
3035
- const matchMediaType = (mediaType, accepts) => {
3036
- return !mediaType || accepts.some((accept) => {
3037
- const trimmedAccept = accept.trim();
3038
- const prefixAccept = trimmedAccept.split('/')[0];
3039
- const suffixAccept = trimmedAccept.split('.')[1];
3040
- return ((trimmedAccept.includes('*') && mediaType.startsWith(prefixAccept)) ||
3041
- (trimmedAccept.includes('.') && mediaType.endsWith(suffixAccept)) ||
3042
- (trimmedAccept === mediaType));
3043
- });
3044
- };
3045
- const partitionArray = (inputVal, validatorFn) => {
3046
- const value = toArray(inputVal);
3047
- if (value == null) {
3048
- return [[], [value]];
3049
- }
3050
- return value.reduce((acc, x) => {
3051
- if (acc[1].length == 0) {
3052
- const r = validatorFn(x);
3053
- const index = r.valid ? 0 : 1;
3054
- acc[index].push(r.value);
3055
- }
3056
- return acc;
3057
- }, [[], []]);
3058
- };
3059
- const ValidConstraints = {
3060
- date: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'format'],
3061
- string: ['minLength', 'maxLength', 'pattern'],
3062
- number: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
3063
- array: ['minItems', 'maxItems', 'uniqueItems'],
3064
- file: ['accept', 'maxFileSize'],
3065
- email: ['minLength', 'maxLength', 'format', 'pattern']
3066
- };
3067
- const Constraints = {
3068
- type: (constraint, inputVal) => {
3069
- let value = inputVal;
3070
- if (inputVal == undefined) {
3071
- return {
3072
- valid: true,
3073
- value: inputVal
3074
- };
3136
+
3137
+ function getStringToNumberFn(locale) {
3138
+ if (locale == null) {
3139
+ const localeOptions = new Intl.DateTimeFormat().resolvedOptions();
3140
+ locale = localeOptions.locale;
3141
+ }
3142
+ return (str) => stringToNumber(str, locale);
3143
+ }
3144
+ class RuleEngine {
3145
+ _context;
3146
+ _globalNames = [
3147
+ '$form',
3148
+ '$field',
3149
+ '$event'
3150
+ ];
3151
+ customFunctions;
3152
+ debugInfo = [];
3153
+ constructor() {
3154
+ this.customFunctions = FunctionRuntime.getFunctions();
3155
+ }
3156
+ compileRule(rule, locale) {
3157
+ const formula = new Formula(this.customFunctions, getStringToNumberFn(locale), this.debugInfo);
3158
+ return { formula, ast: formula.compile(rule, this._globalNames) };
3159
+ }
3160
+ execute(node, data, globals, useValueOf = false) {
3161
+ const { formula, ast } = node;
3162
+ const oldContext = this._context;
3163
+ this._context = globals;
3164
+ let res = undefined;
3165
+ try {
3166
+ res = formula.run(ast, data, 'en-US', globals);
3075
3167
  }
3076
- let valid = true, res;
3077
- switch (constraint) {
3078
- case 'string':
3079
- valid = true;
3080
- value = inputVal.toString();
3081
- break;
3082
- case 'string[]':
3083
- value = toArray(inputVal);
3084
- break;
3085
- case 'number':
3086
- res = checkNumber(inputVal);
3087
- value = res.value;
3088
- valid = res.valid;
3089
- break;
3090
- case 'boolean':
3091
- res = checkBool(inputVal);
3092
- valid = res.valid;
3093
- value = res.value;
3094
- break;
3095
- case 'integer':
3096
- res = checkInteger(inputVal);
3097
- valid = res.valid;
3098
- value = res.value;
3099
- break;
3100
- case 'integer[]':
3101
- res = partitionArray(inputVal, checkInteger);
3102
- valid = res[1].length === 0;
3103
- value = valid ? res[0] : inputVal;
3104
- break;
3105
- case 'file':
3106
- res = checkFile(inputVal instanceof Array ? inputVal[0] : inputVal);
3107
- valid = res.valid;
3108
- value = res.value;
3109
- break;
3110
- case 'file[]':
3111
- res = partitionArray(inputVal, checkFile);
3112
- valid = res[1].length === 0;
3113
- value = valid ? res[0] : inputVal;
3114
- break;
3115
- case 'number[]':
3116
- res = partitionArray(inputVal, checkNumber);
3117
- valid = res[1].length === 0;
3118
- value = valid ? res[0] : inputVal;
3119
- break;
3120
- case 'boolean[]':
3121
- res = partitionArray(inputVal, checkBool);
3122
- valid = res[1].length === 0;
3123
- value = valid ? res[0] : inputVal;
3124
- break;
3168
+ catch (err) {
3169
+ this._context?.form?.logger?.error(err);
3125
3170
  }
3126
- return {
3127
- valid,
3128
- value
3129
- };
3130
- },
3131
- format: (constraint, input) => {
3132
- let valid = true;
3133
- const value = input;
3134
- if (input === null) {
3135
- return { value, valid };
3171
+ while (this.debugInfo.length > 0) {
3172
+ this._context?.form?.logger?.debug(this.debugInfo.pop());
3136
3173
  }
3137
- let res;
3138
- switch (constraint) {
3139
- case 'date':
3140
- res = dateRegex.exec((input || '').trim());
3141
- if (res != null) {
3142
- const [match, year, month, date] = res;
3143
- const [nMonth, nDate] = [+month, +date];
3144
- const leapYear = isLeapYear(+year);
3145
- valid = (nMonth >= 1 && nMonth <= 12) &&
3146
- (nDate >= 1 && nDate <= daysInMonth(leapYear, nMonth));
3147
- }
3148
- else {
3149
- valid = false;
3150
- }
3151
- break;
3152
- case 'email':
3153
- valid = new RegExp(emailRegex).test((input || '').trim());
3154
- break;
3155
- case 'data-url':
3156
- valid = true;
3157
- break;
3174
+ let finalRes = res;
3175
+ if (useValueOf) {
3176
+ if (typeof res === 'object' && res !== null) {
3177
+ finalRes = Object.getPrototypeOf(res).valueOf.call(res);
3178
+ }
3158
3179
  }
3159
- return { valid, value };
3160
- },
3161
- minimum: (constraint, value) => {
3162
- return { valid: value >= constraint, value };
3163
- },
3164
- maximum: (constraint, value) => {
3165
- return { valid: value <= constraint, value };
3166
- },
3167
- exclusiveMinimum: (constraint, value) => {
3168
- return { valid: value > constraint, value };
3169
- },
3170
- exclusiveMaximum: (constraint, value) => {
3171
- return { valid: value < constraint, value };
3172
- },
3173
- minItems: (constraint, value) => {
3174
- return { valid: (value instanceof Array) && value.length >= constraint, value };
3175
- },
3176
- maxItems: (constraint, value) => {
3177
- return { valid: (value instanceof Array) && value.length <= constraint, value };
3178
- },
3179
- uniqueItems: (constraint, value) => {
3180
- return { valid: !constraint || ((value instanceof Array) && value.length === new Set(value).size), value };
3181
- },
3182
- minLength: (constraint, value) => {
3183
- return { ...Constraints.minimum(constraint, typeof value === 'string' ? value.length : 0), value };
3184
- },
3185
- maxLength: (constraint, value) => {
3186
- return { ...Constraints.maximum(constraint, typeof value === 'string' ? value.length : 0), value };
3187
- },
3188
- pattern: (constraint, value) => {
3189
- let regex;
3190
- if (typeof constraint === 'string') {
3191
- 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);
3192
3186
  }
3193
- else {
3194
- 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());
3195
3197
  }
3196
- return { valid: regex.test(value), value };
3197
- },
3198
- required: (constraint, value) => {
3199
- const valid = constraint ? value != null && value !== '' : true;
3200
- return { valid, value };
3201
- },
3202
- enum: (constraint, value) => {
3198
+ }
3199
+ _getDefaults() {
3203
3200
  return {
3204
- valid: constraint.indexOf(value) > -1,
3205
- value
3201
+ ...super._getDefaults(),
3202
+ visible: true,
3203
+ required: false,
3204
+ label: {
3205
+ visible: true,
3206
+ richText: false
3207
+ }
3206
3208
  };
3207
- },
3208
- accept: (constraint, value) => {
3209
- if (!constraint || constraint.length === 0 || value === null || value === undefined) {
3210
- return {
3211
- valid: true,
3212
- value
3213
- };
3209
+ }
3210
+ _applyDefaults() {
3211
+ super._applyDefaultsInModel();
3212
+ if (this._jsonModel.dataRef && this._jsonModel.type === undefined) {
3213
+ this._jsonModel.type = 'object';
3214
3214
  }
3215
- const tempValue = value instanceof Array ? value : [value];
3216
- const invalidFile = tempValue.some((file) => !matchMediaType(file.type, constraint));
3217
- return {
3218
- valid: !invalidFile,
3219
- value
3220
- };
3221
- },
3222
- maxFileSize: (constraint, value) => {
3223
- const sizeLimit = typeof constraint === 'string' ? getFileSizeInBytes(constraint) : constraint;
3224
- return {
3225
- valid: !(value instanceof FileObject) || value.size <= sizeLimit,
3226
- value
3227
- };
3228
3215
  }
3229
- };
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);
3230
3257
 
3231
3258
  const validTypes = ['string', 'number', 'integer', 'boolean', 'file', 'string[]', 'number[]', 'integer[]', 'boolean[]', 'file[]', 'array', 'object'];
3232
3259
  class Field extends Scriptable {
@@ -3451,7 +3478,6 @@ class Field extends Scriptable {
3451
3478
  }
3452
3479
  set required(r) {
3453
3480
  this._setProperty('required', r);
3454
- this.validate();
3455
3481
  }
3456
3482
  get maximum() {
3457
3483
  if (this.type === 'number' || this.format === 'date' || this.type === 'integer') {