@aemforms/af-core 0.22.74 → 0.22.76

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,6 +578,529 @@ const resolveData = (data, input, create) => {
578
578
  return result;
579
579
  };
580
580
 
581
+ class FileObject {
582
+ data;
583
+ mediaType = 'application/octet-stream';
584
+ name = 'unknown';
585
+ size = 0;
586
+ constructor(init) {
587
+ Object.assign(this, init);
588
+ }
589
+ get type() {
590
+ return this.mediaType;
591
+ }
592
+ toJSON() {
593
+ return {
594
+ 'name': this.name,
595
+ 'size': this.size,
596
+ 'mediaType': this.mediaType,
597
+ 'data': this.data.toString()
598
+ };
599
+ }
600
+ equals(obj) {
601
+ return (this.data === obj.data &&
602
+ this.mediaType === obj.mediaType &&
603
+ this.name === obj.name &&
604
+ this.size === obj.size);
605
+ }
606
+ }
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));
616
+ }
617
+ else {
618
+ randIndex = Math.floor(Math.random() * (chars.length));
619
+ }
620
+ ret.push(chars[randIndex]);
621
+ }
622
+ return ret.join('');
623
+ };
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;
629
+ }
630
+ let ret = null;
631
+ if (item.isContainer) {
632
+ ret = getAttachments(item, excludeUnbound);
633
+ }
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
+ });
645
+ }
646
+ else if (item.value != null) {
647
+ ret[item.id] = { ...item.value, 'dataRef': dataRef };
648
+ }
649
+ }
650
+ }
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());
660
+ }
661
+ }
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));
674
+ }
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);
767
+ }
768
+ return null;
769
+ }
770
+ else {
771
+ return null;
772
+ }
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
+ }
794
+ }
795
+ else {
796
+ return null;
797
+ }
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
+ }
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;
814
+ }
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;
830
+ }
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) {
859
+ return {
860
+ value: '', valid: true
861
+ };
862
+ }
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) {
874
+ return {
875
+ value: '', valid: true
876
+ };
877
+ }
878
+ let value = parseFloat(inputVal);
879
+ const valid = !isNaN(value) && Math.round(value) === value;
880
+ if (!valid) {
881
+ value = inputVal;
882
+ }
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
+
581
1104
  const editableProperties = [
582
1105
  'value',
583
1106
  'label',
@@ -818,7 +1341,11 @@ class BaseNode {
818
1341
  index: this.index,
819
1342
  parent: undefined,
820
1343
  qualifiedName: this.qualifiedName,
821
- repeatable: this.repeatable === true ? true : undefined,
1344
+ ...(this.repeatable === true ? {
1345
+ repeatable: true,
1346
+ minOccur: this.parent.minItems,
1347
+ maxOccur: this.parent.maxItems
1348
+ } : {}),
822
1349
  ':type': this[':type'],
823
1350
  ...(forRestore ? {
824
1351
  _dependents: this._dependents.length ? this._dependents.map(x => x.node.id) : undefined,
@@ -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 [];
@@ -1844,250 +2374,7 @@ class EventQueue {
1844
2374
  this._runningEventCount = {};
1845
2375
  this._isProcessing = false;
1846
2376
  }
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
- };
2377
+ }
2091
2378
 
2092
2379
  const request$1 = (url, data = null, options = {}) => {
2093
2380
  const opts = { ...defaultRequestOptions, ...options };
@@ -2336,6 +2623,26 @@ 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 = [payload, validateForm, submitAs];
2630
+ return FunctionRuntimeImpl.getInstance().getFunctions().submitForm._func.call(undefined, args, data, interpreter);
2631
+ },
2632
+ markFieldAsInvalid: (fieldIdentifier, validationMessage, option) => {
2633
+ if (!option || option.useId) {
2634
+ interpreter.globals.form.getElement(fieldIdentifier)?.markAsInvalid(validationMessage);
2635
+ }
2636
+ else if (option && option.useDataRef) {
2637
+ interpreter.globals.form.visit(function callback(f) {
2638
+ if (f.dataRef === fieldIdentifier) {
2639
+ f.markAsInvalid(validationMessage);
2640
+ }
2641
+ });
2642
+ }
2643
+ else if (option && option.useQualifiedName) {
2644
+ interpreter.globals.form.resolveQualifiedName(fieldIdentifier)?.markAsInvalid(validationMessage);
2645
+ }
2339
2646
  }
2340
2647
  }
2341
2648
  };
@@ -2437,11 +2744,24 @@ class FunctionRuntimeImpl {
2437
2744
  },
2438
2745
  submitForm: {
2439
2746
  _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;
2747
+ let success = null;
2748
+ let error = null;
2749
+ let submit_data;
2750
+ let validate_form;
2751
+ let submit_as;
2752
+ if (args.length > 0 && typeof valueOf(args[0]) === 'object') {
2753
+ submit_data = args.length > 0 ? valueOf(args[0]) : null;
2754
+ validate_form = args.length > 1 ? valueOf(args[1]) : true;
2755
+ submit_as = args.length > 2 ? toString(args[2]) : 'multipart/form-data';
2756
+ }
2757
+ else {
2758
+ interpreter.globals.form.logger.warn('This usage of submitForm is deprecated. Please see the documentation and update');
2759
+ success = toString(args[0]);
2760
+ error = toString(args[1]);
2761
+ submit_as = args.length > 2 ? toString(args[2]) : 'multipart/form-data';
2762
+ submit_data = args.length > 3 ? valueOf(args[3]) : null;
2763
+ validate_form = args.length > 4 ? valueOf(args[4]) : true;
2764
+ }
2445
2765
  interpreter.globals.form.dispatch(new Submit({
2446
2766
  success,
2447
2767
  error,
@@ -2830,403 +3150,125 @@ function stringToNumber(str, language) {
2830
3150
  }
2831
3151
 
2832
3152
  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
- };
3153
+ if (locale == null) {
3154
+ const localeOptions = new Intl.DateTimeFormat().resolvedOptions();
3155
+ locale = localeOptions.locale;
3156
+ }
3157
+ return (str) => stringToNumber(str, locale);
3158
+ }
3159
+ class RuleEngine {
3160
+ _context;
3161
+ _globalNames = [
3162
+ '$form',
3163
+ '$field',
3164
+ '$event'
3165
+ ];
3166
+ customFunctions;
3167
+ debugInfo = [];
3168
+ constructor() {
3169
+ this.customFunctions = FunctionRuntime.getFunctions();
3170
+ }
3171
+ compileRule(rule, locale) {
3172
+ const formula = new Formula(this.customFunctions, getStringToNumberFn(locale), this.debugInfo);
3173
+ return { formula, ast: formula.compile(rule, this._globalNames) };
3174
+ }
3175
+ execute(node, data, globals, useValueOf = false) {
3176
+ const { formula, ast } = node;
3177
+ const oldContext = this._context;
3178
+ this._context = globals;
3179
+ let res = undefined;
3180
+ try {
3181
+ res = formula.run(ast, data, 'en-US', globals);
3075
3182
  }
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;
3183
+ catch (err) {
3184
+ this._context?.form?.logger?.error(err);
3125
3185
  }
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 };
3186
+ while (this.debugInfo.length > 0) {
3187
+ this._context?.form?.logger?.debug(this.debugInfo.pop());
3136
3188
  }
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;
3189
+ let finalRes = res;
3190
+ if (useValueOf) {
3191
+ if (typeof res === 'object' && res !== null) {
3192
+ finalRes = Object.getPrototypeOf(res).valueOf.call(res);
3193
+ }
3158
3194
  }
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);
3195
+ this._context = oldContext;
3196
+ return finalRes;
3197
+ }
3198
+ trackDependency(subscriber) {
3199
+ if (this._context && this._context.field !== undefined && this._context.field !== subscriber) {
3200
+ subscriber._addDependent(this._context.field);
3192
3201
  }
3193
- else {
3194
- regex = constraint;
3202
+ }
3203
+ }
3204
+
3205
+ class Fieldset extends Container {
3206
+ constructor(params, _options) {
3207
+ super(params, _options);
3208
+ if (_options.mode !== 'restore') {
3209
+ this._applyDefaults();
3210
+ this.queueEvent(new Initialize());
3211
+ this.queueEvent(new ExecuteRule());
3195
3212
  }
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) => {
3213
+ }
3214
+ _getDefaults() {
3203
3215
  return {
3204
- valid: constraint.indexOf(value) > -1,
3205
- value
3216
+ ...super._getDefaults(),
3217
+ visible: true,
3218
+ required: false,
3219
+ label: {
3220
+ visible: true,
3221
+ richText: false
3222
+ }
3206
3223
  };
3207
- },
3208
- accept: (constraint, value) => {
3209
- if (!constraint || constraint.length === 0 || value === null || value === undefined) {
3210
- return {
3211
- valid: true,
3212
- value
3213
- };
3224
+ }
3225
+ _applyDefaults() {
3226
+ super._applyDefaultsInModel();
3227
+ if (this._jsonModel.dataRef && this._jsonModel.type === undefined) {
3228
+ this._jsonModel.type = 'object';
3214
3229
  }
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
3230
  }
3229
- };
3231
+ get type() {
3232
+ const ret = super.type;
3233
+ if (ret === 'array' || ret === 'object') {
3234
+ return ret;
3235
+ }
3236
+ return undefined;
3237
+ }
3238
+ get items() {
3239
+ return super.items;
3240
+ }
3241
+ get value() {
3242
+ return null;
3243
+ }
3244
+ get fieldType() {
3245
+ return 'panel';
3246
+ }
3247
+ }
3248
+
3249
+ class InstanceManager extends Fieldset {
3250
+ get maxOccur() {
3251
+ return this._jsonModel.maxItems;
3252
+ }
3253
+ set maxOccur(m) {
3254
+ this.maxItems = m;
3255
+ }
3256
+ get minOccur() {
3257
+ return this.minItems;
3258
+ }
3259
+ addInstance(action) {
3260
+ return this.addItem(action);
3261
+ }
3262
+ removeInstance(action) {
3263
+ return this.removeItem(action);
3264
+ }
3265
+ }
3266
+ __decorate([
3267
+ dependencyTracked()
3268
+ ], InstanceManager.prototype, "maxOccur", null);
3269
+ __decorate([
3270
+ dependencyTracked()
3271
+ ], InstanceManager.prototype, "minOccur", null);
3230
3272
 
3231
3273
  const validTypes = ['string', 'number', 'integer', 'boolean', 'file', 'string[]', 'number[]', 'integer[]', 'boolean[]', 'file[]', 'array', 'object'];
3232
3274
  class Field extends Scriptable {
@@ -3385,6 +3427,9 @@ class Field extends Scriptable {
3385
3427
  get displayFormat() {
3386
3428
  return this.withCategory(this._jsonModel.displayFormat);
3387
3429
  }
3430
+ get displayValueExpression() {
3431
+ return this._jsonModel.displayValueExpression;
3432
+ }
3388
3433
  get placeholder() {
3389
3434
  return this._jsonModel.placeholder;
3390
3435
  }
@@ -3451,7 +3496,6 @@ class Field extends Scriptable {
3451
3496
  }
3452
3497
  set required(r) {
3453
3498
  this._setProperty('required', r);
3454
- this.validate();
3455
3499
  }
3456
3500
  get maximum() {
3457
3501
  if (this.type === 'number' || this.format === 'date' || this.type === 'integer') {
@@ -3503,6 +3547,9 @@ class Field extends Scriptable {
3503
3547
  }
3504
3548
  }
3505
3549
  get displayValue() {
3550
+ if (this.displayValueExpression && typeof this.displayValueExpression === 'string' && this.displayValueExpression.length !== 0) {
3551
+ return this.executeExpression(this.displayValueExpression);
3552
+ }
3506
3553
  const df = this.displayFormat;
3507
3554
  if (df && this.isNotEmpty(this.value) && this.valid !== false) {
3508
3555
  try {