@wavemaker/angular-codegen 11.14.1-rc.6311 → 11.14.2-rc.6311

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.
@@ -147299,11 +147299,11 @@ const CURRENCY_INFO = {
147299
147299
  }
147300
147300
  };
147301
147301
 
147302
- const $RAF$1$1 = window.requestAnimationFrame;
147302
+ const $RAF$1 = window.requestAnimationFrame;
147303
147303
  const $RAFQueue$1 = [];
147304
147304
  const invokeLater$1 = fn => {
147305
147305
  if (!$RAFQueue$1.length) {
147306
- $RAF$1$1(() => {
147306
+ $RAF$1(() => {
147307
147307
  $RAFQueue$1.forEach(f => f());
147308
147308
  $RAFQueue$1.length = 0;
147309
147309
  });
@@ -147311,7 +147311,23 @@ const invokeLater$1 = fn => {
147311
147311
  $RAFQueue$1.push(fn);
147312
147312
  };
147313
147313
  const setAttr$1 = (node, attrName, val, sync) => {
147314
- const task = () => node instanceof Element && node.setAttribute(attrName, val);
147314
+ const task = () => {
147315
+ if (node instanceof Element) {
147316
+ // Handle tabindex specifically - ensure it's a valid string representation
147317
+ if (attrName === 'tabindex') {
147318
+ // Convert to string, handling 0, null, undefined, and NaN
147319
+ if (val === null || val === undefined || (typeof val === 'number' && isNaN(val))) {
147320
+ node.removeAttribute(attrName);
147321
+ }
147322
+ else {
147323
+ node.setAttribute(attrName, String(val));
147324
+ }
147325
+ }
147326
+ else {
147327
+ node.setAttribute(attrName, val);
147328
+ }
147329
+ }
147330
+ };
147315
147331
  invokeLater$1(task);
147316
147332
  };
147317
147333
 
@@ -147845,70 +147861,220 @@ const getFnForEventExpr$1 = (expr) => {
147845
147861
  return fnExecutor$1(expr, ExpressionType$2.Action);
147846
147862
  };
147847
147863
 
147864
+ // Constants
147865
+ const WIDGET_ID_REGEX$1 = /^(widget-[^_]+)/;
147866
+ const WIDGET_PROPERTY_REGEX$1 = /^widget-[^_]+_(.+)$/;
147867
+ const ARRAY_INDEX_PLACEHOLDER$1 = '[$i]';
147868
+ const ARRAY_INDEX_ZERO$1 = '[0]';
147848
147869
  const registry$1 = new Map();
147849
147870
  const watchIdGenerator$1 = new IDGenerator$1('watch-id-');
147850
- const FIRST_TIME_WATCH$1 = {};
147851
- Object.freeze(FIRST_TIME_WATCH$1);
147871
+ const FIRST_TIME_WATCH$1 = Object.freeze({});
147872
+ /**
147873
+ * Extracts widget ID from identifier (e.g., "widget-id23_eventsource" -> "widget-id23")
147874
+ */
147875
+ const getWidgetId$1 = (identifier) => {
147876
+ if (!identifier || typeof identifier !== 'string') {
147877
+ return null;
147878
+ }
147879
+ const match = identifier.match(WIDGET_ID_REGEX$1);
147880
+ return match ? match[1] : null;
147881
+ };
147882
+ /**
147883
+ * Extracts property name from identifier (e.g., "widget-id23_eventsource" -> "eventsource")
147884
+ */
147885
+ const getPropertyName$1 = (identifier) => {
147886
+ if (!identifier || typeof identifier !== 'string') {
147887
+ return identifier;
147888
+ }
147889
+ const match = identifier.match(WIDGET_PROPERTY_REGEX$1);
147890
+ return match ? match[1] : identifier;
147891
+ };
147892
+ /**
147893
+ * Array consumer wrapper for array-based expressions
147894
+ */
147852
147895
  const arrayConsumer$1 = (listenerFn, restExpr, newVal, oldVal) => {
147853
- let data = newVal, formattedData;
147854
- if (isArray$1(data)) {
147855
- formattedData = data.map(function (datum) {
147856
- return findValueOf$1(datum, restExpr);
147857
- });
147858
- // If resulting structure is an array of array, flatten it
147859
- if (isArray$1(formattedData[0])) {
147860
- formattedData = flatten$2(formattedData);
147861
- }
147862
- listenerFn(formattedData, oldVal);
147896
+ if (!isArray$1(newVal)) {
147897
+ return;
147898
+ }
147899
+ let formattedData = newVal.map(datum => findValueOf$1(datum, restExpr));
147900
+ // Flatten if result is array of arrays
147901
+ if (isArray$1(formattedData[0])) {
147902
+ formattedData = flatten$2(formattedData);
147863
147903
  }
147904
+ listenerFn(formattedData, oldVal);
147864
147905
  };
147865
- const getUpdatedWatcInfo$1 = (expr, acceptsArray, listener) => {
147866
- // listener doesn't accept array
147867
- // replace all `[$i]` with `[0]` and return the expression
147868
- let regex = /\[\$i\]/g, $I = '[$i]', $0 = '[0]';
147906
+ /**
147907
+ * Updates watch info for array expressions
147908
+ */
147909
+ const getUpdatedWatchInfo$1 = (expr, acceptsArray, listener) => {
147910
+ const regex = /\[\$i\]/g;
147869
147911
  if (!acceptsArray) {
147870
147912
  return {
147871
- 'expr': expr.replace(regex, $0),
147872
- 'listener': listener
147913
+ expr: expr.replace(regex, ARRAY_INDEX_ZERO$1),
147914
+ listener
147873
147915
  };
147874
147916
  }
147875
- // listener accepts array
147876
- // replace all except the last `[$i]` with `[0]` and return the expression.
147877
- var index = expr.lastIndexOf($I), _expr = expr.substr(0, index).replace($I, $0), restExpr = expr.substr(index + 5), arrayConsumerFn = listener;
147878
- if (restExpr) {
147879
- arrayConsumerFn = arrayConsumer$1.bind(undefined, listener, restExpr);
147880
- }
147917
+ const lastIndex = expr.lastIndexOf(ARRAY_INDEX_PLACEHOLDER$1);
147918
+ const baseExpr = expr.substring(0, lastIndex).replace(ARRAY_INDEX_PLACEHOLDER$1, ARRAY_INDEX_ZERO$1);
147919
+ const restExpr = expr.substring(lastIndex + 5);
147920
+ const arrayConsumerFn = restExpr
147921
+ ? arrayConsumer$1.bind(undefined, listener, restExpr)
147922
+ : listener;
147881
147923
  return {
147882
- 'expr': _expr,
147883
- 'listener': arrayConsumerFn
147924
+ expr: baseExpr,
147925
+ listener: arrayConsumerFn
147884
147926
  };
147885
147927
  };
147928
+ /**
147929
+ * Determines if an expression is static (doesn't need to be watched)
147930
+ */
147931
+ const STATIC_EXPRESSION_NAMES$1 = [
147932
+ "row.getProperty('investment')",
147933
+ "row.getProperty('factsheetLink')",
147934
+ "row.getProperty('isRebalanceEligible')"
147935
+ ];
147936
+ const isStaticExpression$1 = (expr) => {
147937
+ if (typeof expr !== 'string') {
147938
+ return false;
147939
+ }
147940
+ const trimmedExpr = expr.trim();
147941
+ // Expressions that always evaluate to localization strings
147942
+ // if (trimmedExpr.includes('appLocale')) {
147943
+ // return true;
147944
+ // }
147945
+ // Hard-coded static expression names
147946
+ if (STATIC_EXPRESSION_NAMES$1.includes(trimmedExpr)) {
147947
+ return true;
147948
+ }
147949
+ return false;
147950
+ };
147951
+ /**
147952
+ * Gets the scope type from the scope object
147953
+ */
147954
+ const getScopeType$1 = ($scope) => {
147955
+ if (!$scope) {
147956
+ return null;
147957
+ }
147958
+ if ($scope.pageName)
147959
+ return 'Page';
147960
+ if ($scope.prefabName)
147961
+ return 'Prefab';
147962
+ if ($scope.partialName)
147963
+ return 'Partial';
147964
+ // Check for App scope
147965
+ if ($scope.Variables !== undefined &&
147966
+ $scope.Actions !== undefined &&
147967
+ !$scope.pageName &&
147968
+ !$scope.prefabName &&
147969
+ !$scope.partialName) {
147970
+ return 'App';
147971
+ }
147972
+ if ($scope.constructor?.name === 'AppRef') {
147973
+ return 'App';
147974
+ }
147975
+ return null;
147976
+ };
147977
+ /**
147978
+ * Gets scope name based on scope type
147979
+ */
147980
+ const getScopeName$1 = ($scope, scopeType) => {
147981
+ if (!scopeType || !$scope) {
147982
+ return null;
147983
+ }
147984
+ switch (scopeType) {
147985
+ case 'Prefab': return $scope.prefabName || null;
147986
+ case 'Partial': return $scope.partialName || null;
147987
+ case 'Page': return $scope.pageName || null;
147988
+ default: return null;
147989
+ }
147990
+ };
147991
+ /**
147992
+ * Main watch function
147993
+ */
147886
147994
  const $watch$1 = (expr, $scope, $locals, listener, identifier = watchIdGenerator$1.nextUid(), doNotClone = false, config = {}, isMuted) => {
147887
- if (expr.indexOf('[$i]') !== -1) {
147888
- let watchInfo = getUpdatedWatcInfo$1(expr, config && (config.arrayType || config.isList), listener);
147995
+ // Handle array expressions
147996
+ if (expr.includes(ARRAY_INDEX_PLACEHOLDER$1)) {
147997
+ const watchInfo = getUpdatedWatchInfo$1(expr, config.arrayType || config.isList || false, listener);
147889
147998
  expr = watchInfo.expr;
147890
147999
  listener = watchInfo.listener;
147891
148000
  }
148001
+ // Handle static expressions
148002
+ if (isStaticExpression$1(expr)) {
148003
+ try {
148004
+ const fn = $parseExpr$1(expr);
148005
+ const staticValue = fn($scope, $locals);
148006
+ listener(staticValue, FIRST_TIME_WATCH$1);
148007
+ }
148008
+ catch (e) {
148009
+ console.warn(`Error evaluating static expression '${expr}':`, e);
148010
+ listener(undefined, FIRST_TIME_WATCH$1);
148011
+ }
148012
+ return () => { }; // No-op unsubscribe
148013
+ }
147892
148014
  const fn = $parseExpr$1();
147893
- registry$1.set(identifier, {
147894
- fn: fn.bind(expr, $scope, $locals),
148015
+ const scopeType = getScopeType$1($scope);
148016
+ const scopeName = getScopeName$1($scope, scopeType);
148017
+ const watchInfo = {
148018
+ fn: fn.bind(null, $scope, $locals),
147895
148019
  listener,
147896
148020
  expr,
147897
148021
  last: FIRST_TIME_WATCH$1,
147898
148022
  doNotClone,
147899
- isMuted: isMuted
147900
- });
148023
+ isMuted,
148024
+ scopeType,
148025
+ scopeName
148026
+ };
148027
+ // Store in registry
148028
+ const widgetId = getWidgetId$1(identifier);
148029
+ if (widgetId) {
148030
+ const propertyName = getPropertyName$1(identifier);
148031
+ if (!registry$1.has(widgetId)) {
148032
+ registry$1.set(widgetId, {});
148033
+ }
148034
+ const widgetGroup = registry$1.get(widgetId);
148035
+ widgetGroup[propertyName] = watchInfo;
148036
+ }
148037
+ else {
148038
+ registry$1.set(identifier, watchInfo);
148039
+ }
147901
148040
  return () => $unwatch$1(identifier);
147902
148041
  };
147903
- const $unwatch$1 = identifier => registry$1.delete(identifier);
147904
- window.watchRegistry = registry$1;
148042
+ /**
148043
+ * Unwatches a single identifier
148044
+ */
148045
+ const $unwatch$1 = (identifier) => {
148046
+ const widgetId = getWidgetId$1(identifier);
148047
+ if (widgetId) {
148048
+ const propertyName = getPropertyName$1(identifier);
148049
+ const widgetGroup = registry$1.get(widgetId);
148050
+ if (widgetGroup && typeof widgetGroup === 'object' && !widgetGroup.fn) {
148051
+ const watchInfo = widgetGroup[propertyName];
148052
+ if (watchInfo) {
148053
+ delete widgetGroup[propertyName];
148054
+ // Clean up empty widget groups
148055
+ if (Object.keys(widgetGroup).length === 0) {
148056
+ registry$1.delete(widgetId);
148057
+ }
148058
+ return true;
148059
+ }
148060
+ }
148061
+ }
148062
+ // Fallback to direct lookup
148063
+ if (registry$1.has(identifier)) {
148064
+ registry$1.delete(identifier);
148065
+ return true;
148066
+ }
148067
+ return false;
148068
+ };
147905
148069
  const $appDigest$1 = (() => {
147906
- return (force) => {
148070
+ return (force = false) => {
147907
148071
  {
147908
148072
  return;
147909
148073
  }
147910
148074
  };
147911
148075
  })();
148076
+ // Export registry for debugging
148077
+ window.watchRegistry = registry$1;
147912
148078
 
147913
148079
  var ComponentType$1;
147914
148080
  (function (ComponentType) {
@@ -147964,8 +148130,8 @@ var Operation$1;
147964
148130
  const DataSource$1 = {
147965
148131
  Operation: Operation$1
147966
148132
  };
147967
- class App {
147968
- }
148133
+ let App$1 = class App {
148134
+ };
147969
148135
  let AbstractI18nService$1 = class AbstractI18nService {
147970
148136
  };
147971
148137
 
@@ -147983,6 +148149,7 @@ const REGEX$1 = {
147983
148149
  SUPPORTED_AUDIO_FORMAT: /\.(mp3|ogg|webm|wma|3gp|wav|m4a)$/i,
147984
148150
  SUPPORTED_VIDEO_FORMAT: /\.(mp4|ogg|webm|wmv|mpeg|mpg|avi|mov)$/i,
147985
148151
  VALID_WEB_URL: /^(http[s]?:\/\/)(www\.){0,1}[a-zA-Z0-9=:?\/\.\-]+(\.[a-zA-Z]{2,5}[\.]{0,1})?/,
148152
+ VALID_IMAGE_URL: /^(https?|blob|data|file|ftp):/i,
147986
148153
  REPLACE_PATTERN: /\$\{([^\}]+)\}/g,
147987
148154
  DATA_URL: /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*)\s*$/i,
147988
148155
  ISO_DATE_FORMAT: /(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(\.\d+)?([+-]\d{2}:?\d{2}|Z)$/
@@ -148205,6 +148372,9 @@ const isVideoFile$1 = (fileName) => {
148205
148372
  const isValidWebURL$1 = (url) => {
148206
148373
  return (REGEX$1.VALID_WEB_URL).test(url);
148207
148374
  };
148375
+ const isValidImageUrl$1 = (url) => {
148376
+ return (REGEX$1.VALID_IMAGE_URL).test(url?.trim());
148377
+ };
148208
148378
  /*This function returns the url to the resource after checking the validity of url*/
148209
148379
  const getResourceURL$1 = (urlString) => {
148210
148380
  return urlString;
@@ -149458,6 +149628,7 @@ var Utils$1 = /*#__PURE__*/Object.freeze({
149458
149628
  isPageable: isPageable$1,
149459
149629
  isSafari: isSafari$1,
149460
149630
  isTablet: isTablet$1,
149631
+ isValidImageUrl: isValidImageUrl$1,
149461
149632
  isValidWebURL: isValidWebURL$1,
149462
149633
  isVideoFile: isVideoFile$1,
149463
149634
  loadScript: loadScript$1,
@@ -149551,7 +149722,10 @@ let EventNotifier$1 = class EventNotifier {
149551
149722
  return noop$6;
149552
149723
  }
149553
149724
  destroy() {
149554
- this._subject.complete();
149725
+ this._subject.next(null); // optional
149726
+ this._subject.complete(); // clears all observers
149727
+ this._isInitialized = false;
149728
+ this._eventsBeforeInit = [];
149555
149729
  }
149556
149730
  };
149557
149731
  let MINIMUM_TAB_WIDTH$1 = 768;
@@ -149566,9 +149740,11 @@ let Viewport$1 = class Viewport {
149566
149740
  this.isTabletType = false;
149567
149741
  this._eventNotifier = new EventNotifier$1(true);
149568
149742
  this.setScreenType();
149569
- window.addEventListener("resize" /* ViewportEvent.RESIZE */, this.resizeFn.bind(this));
149570
- const query = window.matchMedia('(orientation: portrait)');
149571
- if (query.matches) {
149743
+ // MEMORY LEAK FIX: Store bound function reference for proper removal
149744
+ this.boundResizeFn = this.resizeFn.bind(this);
149745
+ window.addEventListener("resize" /* ViewportEvent.RESIZE */, this.boundResizeFn);
149746
+ this.mediaQuery = window.matchMedia('(orientation: portrait)');
149747
+ if (this.mediaQuery.matches) {
149572
149748
  this.orientation.isPortrait = true;
149573
149749
  }
149574
149750
  else {
@@ -149576,8 +149752,10 @@ let Viewport$1 = class Viewport {
149576
149752
  }
149577
149753
  // Add a media query change listener
149578
149754
  // addEventListener is not supported ios browser
149579
- if (query.addEventListener) {
149580
- query.addEventListener('change', $event => this.orientationChange($event, !$event.matches));
149755
+ if (this.mediaQuery.addEventListener) {
149756
+ // MEMORY LEAK FIX: Store bound function reference for proper removal
149757
+ this.boundOrientationChange = ($event) => this.orientationChange($event, !$event.matches);
149758
+ this.mediaQuery.addEventListener('change', this.boundOrientationChange);
149581
149759
  }
149582
149760
  }
149583
149761
  update(selectedViewPort) {
@@ -149604,14 +149782,14 @@ let Viewport$1 = class Viewport {
149604
149782
  return this._eventNotifier.subscribe(eventName, callback);
149605
149783
  }
149606
149784
  notify(eventName, ...data) {
149607
- this._eventNotifier.notify.apply(this._eventNotifier, arguments);
149785
+ this._eventNotifier.notify(eventName, ...data);
149608
149786
  }
149609
149787
  setScreenType() {
149610
149788
  const $el = document.querySelector('.wm-app');
149611
149789
  this.screenWidth = $el.clientWidth;
149612
149790
  this.screenHeight = $el.clientHeight;
149613
- this.screenWidth < this.screenHeight ? this.screenWidth : this.screenHeight;
149614
- this.screenWidth > this.screenHeight ? this.screenWidth : this.screenHeight;
149791
+ // const minValue = this.screenWidth < this.screenHeight ? this.screenWidth : this.screenHeight;
149792
+ // const maxValue = this.screenWidth > this.screenHeight ? this.screenWidth : this.screenHeight;
149615
149793
  this.isTabletType = false;
149616
149794
  this.isMobileType = false;
149617
149795
  if (get$2(this.selectedViewPort, 'deviceCategory')) {
@@ -149643,7 +149821,17 @@ let Viewport$1 = class Viewport {
149643
149821
  }
149644
149822
  ngOnDestroy() {
149645
149823
  this._eventNotifier.destroy();
149646
- window.removeEventListener('resize', this.resizeFn);
149824
+ // MEMORY LEAK FIX: Remove resize event listener using stored bound function reference
149825
+ if (this.boundResizeFn) {
149826
+ window.removeEventListener('resize', this.boundResizeFn);
149827
+ this.boundResizeFn = null;
149828
+ }
149829
+ // MEMORY LEAK FIX: Remove media query orientation change listener
149830
+ if (this.mediaQuery && this.boundOrientationChange && this.mediaQuery.removeEventListener) {
149831
+ this.mediaQuery.removeEventListener('change', this.boundOrientationChange);
149832
+ this.boundOrientationChange = null;
149833
+ }
149834
+ this.mediaQuery = null;
149647
149835
  }
149648
149836
  static { this.ɵfac = ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: Viewport, deps: [], target: FactoryTarget$2.Injectable }); }
149649
149837
  static { this.ɵprov = ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: Viewport, providedIn: 'root' }); }
@@ -150699,7 +150887,7 @@ class ToDatePipe extends WmPipe {
150699
150887
  this.app = app;
150700
150888
  this.customPipeManager = customPipeManager;
150701
150889
  }
150702
- static { this.ɵfac = function ToDatePipe_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ToDatePipe)(ɵɵdirectiveInject(DatePipe, 16), ɵɵdirectiveInject(AbstractI18nService$1, 16), ɵɵdirectiveInject(App, 16), ɵɵdirectiveInject(CustomPipeManager$1, 16)); }; }
150890
+ static { this.ɵfac = function ToDatePipe_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ToDatePipe)(ɵɵdirectiveInject(DatePipe, 16), ɵɵdirectiveInject(AbstractI18nService$1, 16), ɵɵdirectiveInject(App$1, 16), ɵɵdirectiveInject(CustomPipeManager$1, 16)); }; }
150703
150891
  static { this.ɵpipe = /*@__PURE__*/ ɵɵdefinePipe({ name: "toDate", type: ToDatePipe, pure: true, standalone: true }); }
150704
150892
  }
150705
150893
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ToDatePipe, [{
@@ -150708,7 +150896,7 @@ class ToDatePipe extends WmPipe {
150708
150896
  standalone: true,
150709
150897
  name: 'toDate'
150710
150898
  }]
150711
- }], () => [{ type: DatePipe }, { type: AbstractI18nService$1 }, { type: App }, { type: CustomPipeManager$1 }], null); })();
150899
+ }], () => [{ type: DatePipe }, { type: AbstractI18nService$1 }, { type: App$1 }, { type: CustomPipeManager$1 }], null); })();
150712
150900
  class ToNumberPipe {
150713
150901
  transform(data, fracSize) {
150714
150902
  if (fracSize && !String(fracSize).match(/^(\d+)?\.((\d+)(-(\d+))?)?$/)) {
@@ -150818,7 +151006,7 @@ class CustomPipe {
150818
151006
  return data;
150819
151007
  }
150820
151008
  }
150821
- static { this.ɵfac = function CustomPipe_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || CustomPipe)(ɵɵdirectiveInject(App, 16), ɵɵdirectiveInject(CustomPipeManager$1, 16)); }; }
151009
+ static { this.ɵfac = function CustomPipe_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || CustomPipe)(ɵɵdirectiveInject(App$1, 16), ɵɵdirectiveInject(CustomPipeManager$1, 16)); }; }
150822
151010
  static { this.ɵpipe = /*@__PURE__*/ ɵɵdefinePipe({ name: "custom", type: CustomPipe, pure: true, standalone: true }); }
150823
151011
  }
150824
151012
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CustomPipe, [{
@@ -150827,7 +151015,7 @@ class CustomPipe {
150827
151015
  standalone: true,
150828
151016
  name: 'custom'
150829
151017
  }]
150830
- }], () => [{ type: App }, { type: CustomPipeManager$1 }], null); })();
151018
+ }], () => [{ type: App$1 }, { type: CustomPipeManager$1 }], null); })();
150831
151019
  class TimeFromNowPipe {
150832
151020
  transform(data) {
150833
151021
  let timestamp;
@@ -202909,11 +203097,11 @@ var DEFAULT_FORMATS;
202909
203097
  DEFAULT_FORMATS["DATE_TIME"] = "yyyy-MM-dd HH:mm:ss";
202910
203098
  })(DEFAULT_FORMATS || (DEFAULT_FORMATS = {}));
202911
203099
 
202912
- const $RAF$1 = window.requestAnimationFrame;
203100
+ const $RAF = window.requestAnimationFrame;
202913
203101
  const $RAFQueue = [];
202914
203102
  const invokeLater = fn => {
202915
203103
  if (!$RAFQueue.length) {
202916
- $RAF$1(() => {
203104
+ $RAF(() => {
202917
203105
  $RAFQueue.forEach(f => f());
202918
203106
  $RAFQueue.length = 0;
202919
203107
  });
@@ -202921,7 +203109,23 @@ const invokeLater = fn => {
202921
203109
  $RAFQueue.push(fn);
202922
203110
  };
202923
203111
  const setAttr = (node, attrName, val, sync) => {
202924
- const task = () => node instanceof Element && node.setAttribute(attrName, val);
203112
+ const task = () => {
203113
+ if (node instanceof Element) {
203114
+ // Handle tabindex specifically - ensure it's a valid string representation
203115
+ if (attrName === 'tabindex') {
203116
+ // Convert to string, handling 0, null, undefined, and NaN
203117
+ if (val === null || val === undefined || (typeof val === 'number' && isNaN(val))) {
203118
+ node.removeAttribute(attrName);
203119
+ }
203120
+ else {
203121
+ node.setAttribute(attrName, String(val));
203122
+ }
203123
+ }
203124
+ else {
203125
+ node.setAttribute(attrName, val);
203126
+ }
203127
+ }
203128
+ };
202925
203129
  invokeLater(task);
202926
203130
  };
202927
203131
 
@@ -203455,70 +203659,220 @@ const getFnForEventExpr = (expr) => {
203455
203659
  return fnExecutor(expr, ExpressionType.Action);
203456
203660
  };
203457
203661
 
203662
+ // Constants
203663
+ const WIDGET_ID_REGEX = /^(widget-[^_]+)/;
203664
+ const WIDGET_PROPERTY_REGEX = /^widget-[^_]+_(.+)$/;
203665
+ const ARRAY_INDEX_PLACEHOLDER = '[$i]';
203666
+ const ARRAY_INDEX_ZERO = '[0]';
203458
203667
  const registry = new Map();
203459
203668
  const watchIdGenerator = new IDGenerator('watch-id-');
203460
- const FIRST_TIME_WATCH = {};
203461
- Object.freeze(FIRST_TIME_WATCH);
203669
+ const FIRST_TIME_WATCH = Object.freeze({});
203670
+ /**
203671
+ * Extracts widget ID from identifier (e.g., "widget-id23_eventsource" -> "widget-id23")
203672
+ */
203673
+ const getWidgetId = (identifier) => {
203674
+ if (!identifier || typeof identifier !== 'string') {
203675
+ return null;
203676
+ }
203677
+ const match = identifier.match(WIDGET_ID_REGEX);
203678
+ return match ? match[1] : null;
203679
+ };
203680
+ /**
203681
+ * Extracts property name from identifier (e.g., "widget-id23_eventsource" -> "eventsource")
203682
+ */
203683
+ const getPropertyName = (identifier) => {
203684
+ if (!identifier || typeof identifier !== 'string') {
203685
+ return identifier;
203686
+ }
203687
+ const match = identifier.match(WIDGET_PROPERTY_REGEX);
203688
+ return match ? match[1] : identifier;
203689
+ };
203690
+ /**
203691
+ * Array consumer wrapper for array-based expressions
203692
+ */
203462
203693
  const arrayConsumer = (listenerFn, restExpr, newVal, oldVal) => {
203463
- let data = newVal, formattedData;
203464
- if (isArray(data)) {
203465
- formattedData = data.map(function (datum) {
203466
- return findValueOf(datum, restExpr);
203467
- });
203468
- // If resulting structure is an array of array, flatten it
203469
- if (isArray(formattedData[0])) {
203470
- formattedData = flatten(formattedData);
203471
- }
203472
- listenerFn(formattedData, oldVal);
203694
+ if (!isArray(newVal)) {
203695
+ return;
203473
203696
  }
203697
+ let formattedData = newVal.map(datum => findValueOf(datum, restExpr));
203698
+ // Flatten if result is array of arrays
203699
+ if (isArray(formattedData[0])) {
203700
+ formattedData = flatten(formattedData);
203701
+ }
203702
+ listenerFn(formattedData, oldVal);
203474
203703
  };
203475
- const getUpdatedWatcInfo = (expr, acceptsArray, listener) => {
203476
- // listener doesn't accept array
203477
- // replace all `[$i]` with `[0]` and return the expression
203478
- let regex = /\[\$i\]/g, $I = '[$i]', $0 = '[0]';
203704
+ /**
203705
+ * Updates watch info for array expressions
203706
+ */
203707
+ const getUpdatedWatchInfo = (expr, acceptsArray, listener) => {
203708
+ const regex = /\[\$i\]/g;
203479
203709
  if (!acceptsArray) {
203480
203710
  return {
203481
- 'expr': expr.replace(regex, $0),
203482
- 'listener': listener
203711
+ expr: expr.replace(regex, ARRAY_INDEX_ZERO),
203712
+ listener
203483
203713
  };
203484
203714
  }
203485
- // listener accepts array
203486
- // replace all except the last `[$i]` with `[0]` and return the expression.
203487
- var index = expr.lastIndexOf($I), _expr = expr.substr(0, index).replace($I, $0), restExpr = expr.substr(index + 5), arrayConsumerFn = listener;
203488
- if (restExpr) {
203489
- arrayConsumerFn = arrayConsumer.bind(undefined, listener, restExpr);
203490
- }
203715
+ const lastIndex = expr.lastIndexOf(ARRAY_INDEX_PLACEHOLDER);
203716
+ const baseExpr = expr.substring(0, lastIndex).replace(ARRAY_INDEX_PLACEHOLDER, ARRAY_INDEX_ZERO);
203717
+ const restExpr = expr.substring(lastIndex + 5);
203718
+ const arrayConsumerFn = restExpr
203719
+ ? arrayConsumer.bind(undefined, listener, restExpr)
203720
+ : listener;
203491
203721
  return {
203492
- 'expr': _expr,
203493
- 'listener': arrayConsumerFn
203722
+ expr: baseExpr,
203723
+ listener: arrayConsumerFn
203494
203724
  };
203495
203725
  };
203726
+ /**
203727
+ * Determines if an expression is static (doesn't need to be watched)
203728
+ */
203729
+ const STATIC_EXPRESSION_NAMES = [
203730
+ "row.getProperty('investment')",
203731
+ "row.getProperty('factsheetLink')",
203732
+ "row.getProperty('isRebalanceEligible')"
203733
+ ];
203734
+ const isStaticExpression = (expr) => {
203735
+ if (typeof expr !== 'string') {
203736
+ return false;
203737
+ }
203738
+ const trimmedExpr = expr.trim();
203739
+ // Expressions that always evaluate to localization strings
203740
+ // if (trimmedExpr.includes('appLocale')) {
203741
+ // return true;
203742
+ // }
203743
+ // Hard-coded static expression names
203744
+ if (STATIC_EXPRESSION_NAMES.includes(trimmedExpr)) {
203745
+ return true;
203746
+ }
203747
+ return false;
203748
+ };
203749
+ /**
203750
+ * Gets the scope type from the scope object
203751
+ */
203752
+ const getScopeType = ($scope) => {
203753
+ if (!$scope) {
203754
+ return null;
203755
+ }
203756
+ if ($scope.pageName)
203757
+ return 'Page';
203758
+ if ($scope.prefabName)
203759
+ return 'Prefab';
203760
+ if ($scope.partialName)
203761
+ return 'Partial';
203762
+ // Check for App scope
203763
+ if ($scope.Variables !== undefined &&
203764
+ $scope.Actions !== undefined &&
203765
+ !$scope.pageName &&
203766
+ !$scope.prefabName &&
203767
+ !$scope.partialName) {
203768
+ return 'App';
203769
+ }
203770
+ if ($scope.constructor?.name === 'AppRef') {
203771
+ return 'App';
203772
+ }
203773
+ return null;
203774
+ };
203775
+ /**
203776
+ * Gets scope name based on scope type
203777
+ */
203778
+ const getScopeName = ($scope, scopeType) => {
203779
+ if (!scopeType || !$scope) {
203780
+ return null;
203781
+ }
203782
+ switch (scopeType) {
203783
+ case 'Prefab': return $scope.prefabName || null;
203784
+ case 'Partial': return $scope.partialName || null;
203785
+ case 'Page': return $scope.pageName || null;
203786
+ default: return null;
203787
+ }
203788
+ };
203789
+ /**
203790
+ * Main watch function
203791
+ */
203496
203792
  const $watch = (expr, $scope, $locals, listener, identifier = watchIdGenerator.nextUid(), doNotClone = false, config = {}, isMuted) => {
203497
- if (expr.indexOf('[$i]') !== -1) {
203498
- let watchInfo = getUpdatedWatcInfo(expr, config && (config.arrayType || config.isList), listener);
203793
+ // Handle array expressions
203794
+ if (expr.includes(ARRAY_INDEX_PLACEHOLDER)) {
203795
+ const watchInfo = getUpdatedWatchInfo(expr, config.arrayType || config.isList || false, listener);
203499
203796
  expr = watchInfo.expr;
203500
203797
  listener = watchInfo.listener;
203501
203798
  }
203799
+ // Handle static expressions
203800
+ if (isStaticExpression(expr)) {
203801
+ try {
203802
+ const fn = $parseExpr(expr);
203803
+ const staticValue = fn($scope, $locals);
203804
+ listener(staticValue, FIRST_TIME_WATCH);
203805
+ }
203806
+ catch (e) {
203807
+ console.warn(`Error evaluating static expression '${expr}':`, e);
203808
+ listener(undefined, FIRST_TIME_WATCH);
203809
+ }
203810
+ return () => { }; // No-op unsubscribe
203811
+ }
203502
203812
  const fn = $parseExpr();
203503
- registry.set(identifier, {
203504
- fn: fn.bind(expr, $scope, $locals),
203813
+ const scopeType = getScopeType($scope);
203814
+ const scopeName = getScopeName($scope, scopeType);
203815
+ const watchInfo = {
203816
+ fn: fn.bind(null, $scope, $locals),
203505
203817
  listener,
203506
203818
  expr,
203507
203819
  last: FIRST_TIME_WATCH,
203508
203820
  doNotClone,
203509
- isMuted: isMuted
203510
- });
203821
+ isMuted,
203822
+ scopeType,
203823
+ scopeName
203824
+ };
203825
+ // Store in registry
203826
+ const widgetId = getWidgetId(identifier);
203827
+ if (widgetId) {
203828
+ const propertyName = getPropertyName(identifier);
203829
+ if (!registry.has(widgetId)) {
203830
+ registry.set(widgetId, {});
203831
+ }
203832
+ const widgetGroup = registry.get(widgetId);
203833
+ widgetGroup[propertyName] = watchInfo;
203834
+ }
203835
+ else {
203836
+ registry.set(identifier, watchInfo);
203837
+ }
203511
203838
  return () => $unwatch(identifier);
203512
203839
  };
203513
- const $unwatch = identifier => registry.delete(identifier);
203514
- window.watchRegistry = registry;
203840
+ /**
203841
+ * Unwatches a single identifier
203842
+ */
203843
+ const $unwatch = (identifier) => {
203844
+ const widgetId = getWidgetId(identifier);
203845
+ if (widgetId) {
203846
+ const propertyName = getPropertyName(identifier);
203847
+ const widgetGroup = registry.get(widgetId);
203848
+ if (widgetGroup && typeof widgetGroup === 'object' && !widgetGroup.fn) {
203849
+ const watchInfo = widgetGroup[propertyName];
203850
+ if (watchInfo) {
203851
+ delete widgetGroup[propertyName];
203852
+ // Clean up empty widget groups
203853
+ if (Object.keys(widgetGroup).length === 0) {
203854
+ registry.delete(widgetId);
203855
+ }
203856
+ return true;
203857
+ }
203858
+ }
203859
+ }
203860
+ // Fallback to direct lookup
203861
+ if (registry.has(identifier)) {
203862
+ registry.delete(identifier);
203863
+ return true;
203864
+ }
203865
+ return false;
203866
+ };
203515
203867
  const $appDigest = (() => {
203516
- return (force) => {
203868
+ return (force = false) => {
203517
203869
  {
203518
203870
  return;
203519
203871
  }
203520
203872
  };
203521
203873
  })();
203874
+ // Export registry for debugging
203875
+ window.watchRegistry = registry;
203522
203876
 
203523
203877
  var ComponentType;
203524
203878
  (function (ComponentType) {
@@ -203574,6 +203928,8 @@ var Operation;
203574
203928
  const DataSource = {
203575
203929
  Operation
203576
203930
  };
203931
+ class App {
203932
+ }
203577
203933
  class AbstractI18nService {
203578
203934
  }
203579
203935
 
@@ -203591,6 +203947,7 @@ const REGEX = {
203591
203947
  SUPPORTED_AUDIO_FORMAT: /\.(mp3|ogg|webm|wma|3gp|wav|m4a)$/i,
203592
203948
  SUPPORTED_VIDEO_FORMAT: /\.(mp4|ogg|webm|wmv|mpeg|mpg|avi|mov)$/i,
203593
203949
  VALID_WEB_URL: /^(http[s]?:\/\/)(www\.){0,1}[a-zA-Z0-9=:?\/\.\-]+(\.[a-zA-Z]{2,5}[\.]{0,1})?/,
203950
+ VALID_IMAGE_URL: /^(https?|blob|data|file|ftp):/i,
203594
203951
  REPLACE_PATTERN: /\$\{([^\}]+)\}/g,
203595
203952
  DATA_URL: /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*)\s*$/i,
203596
203953
  ISO_DATE_FORMAT: /(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(\.\d+)?([+-]\d{2}:?\d{2}|Z)$/
@@ -203813,6 +204170,9 @@ const isVideoFile = (fileName) => {
203813
204170
  const isValidWebURL = (url) => {
203814
204171
  return (REGEX.VALID_WEB_URL).test(url);
203815
204172
  };
204173
+ const isValidImageUrl = (url) => {
204174
+ return (REGEX.VALID_IMAGE_URL).test(url?.trim());
204175
+ };
203816
204176
  /*This function returns the url to the resource after checking the validity of url*/
203817
204177
  const getResourceURL = (urlString) => {
203818
204178
  return urlString;
@@ -205066,6 +205426,7 @@ var Utils = /*#__PURE__*/Object.freeze({
205066
205426
  isPageable: isPageable,
205067
205427
  isSafari: isSafari,
205068
205428
  isTablet: isTablet,
205429
+ isValidImageUrl: isValidImageUrl,
205069
205430
  isValidWebURL: isValidWebURL,
205070
205431
  isVideoFile: isVideoFile,
205071
205432
  loadScript: loadScript,
@@ -205159,7 +205520,10 @@ class EventNotifier {
205159
205520
  return noop;
205160
205521
  }
205161
205522
  destroy() {
205162
- this._subject.complete();
205523
+ this._subject.next(null); // optional
205524
+ this._subject.complete(); // clears all observers
205525
+ this._isInitialized = false;
205526
+ this._eventsBeforeInit = [];
205163
205527
  }
205164
205528
  }
205165
205529
  let MINIMUM_TAB_WIDTH = 768;
@@ -205174,9 +205538,11 @@ class Viewport {
205174
205538
  this.isTabletType = false;
205175
205539
  this._eventNotifier = new EventNotifier(true);
205176
205540
  this.setScreenType();
205177
- window.addEventListener("resize" /* ViewportEvent.RESIZE */, this.resizeFn.bind(this));
205178
- const query = window.matchMedia('(orientation: portrait)');
205179
- if (query.matches) {
205541
+ // MEMORY LEAK FIX: Store bound function reference for proper removal
205542
+ this.boundResizeFn = this.resizeFn.bind(this);
205543
+ window.addEventListener("resize" /* ViewportEvent.RESIZE */, this.boundResizeFn);
205544
+ this.mediaQuery = window.matchMedia('(orientation: portrait)');
205545
+ if (this.mediaQuery.matches) {
205180
205546
  this.orientation.isPortrait = true;
205181
205547
  }
205182
205548
  else {
@@ -205184,8 +205550,10 @@ class Viewport {
205184
205550
  }
205185
205551
  // Add a media query change listener
205186
205552
  // addEventListener is not supported ios browser
205187
- if (query.addEventListener) {
205188
- query.addEventListener('change', $event => this.orientationChange($event, !$event.matches));
205553
+ if (this.mediaQuery.addEventListener) {
205554
+ // MEMORY LEAK FIX: Store bound function reference for proper removal
205555
+ this.boundOrientationChange = ($event) => this.orientationChange($event, !$event.matches);
205556
+ this.mediaQuery.addEventListener('change', this.boundOrientationChange);
205189
205557
  }
205190
205558
  }
205191
205559
  update(selectedViewPort) {
@@ -205212,14 +205580,14 @@ class Viewport {
205212
205580
  return this._eventNotifier.subscribe(eventName, callback);
205213
205581
  }
205214
205582
  notify(eventName, ...data) {
205215
- this._eventNotifier.notify.apply(this._eventNotifier, arguments);
205583
+ this._eventNotifier.notify(eventName, ...data);
205216
205584
  }
205217
205585
  setScreenType() {
205218
205586
  const $el = document.querySelector('.wm-app');
205219
205587
  this.screenWidth = $el.clientWidth;
205220
205588
  this.screenHeight = $el.clientHeight;
205221
- this.screenWidth < this.screenHeight ? this.screenWidth : this.screenHeight;
205222
- this.screenWidth > this.screenHeight ? this.screenWidth : this.screenHeight;
205589
+ // const minValue = this.screenWidth < this.screenHeight ? this.screenWidth : this.screenHeight;
205590
+ // const maxValue = this.screenWidth > this.screenHeight ? this.screenWidth : this.screenHeight;
205223
205591
  this.isTabletType = false;
205224
205592
  this.isMobileType = false;
205225
205593
  if (get(this.selectedViewPort, 'deviceCategory')) {
@@ -205251,7 +205619,17 @@ class Viewport {
205251
205619
  }
205252
205620
  ngOnDestroy() {
205253
205621
  this._eventNotifier.destroy();
205254
- window.removeEventListener('resize', this.resizeFn);
205622
+ // MEMORY LEAK FIX: Remove resize event listener using stored bound function reference
205623
+ if (this.boundResizeFn) {
205624
+ window.removeEventListener('resize', this.boundResizeFn);
205625
+ this.boundResizeFn = null;
205626
+ }
205627
+ // MEMORY LEAK FIX: Remove media query orientation change listener
205628
+ if (this.mediaQuery && this.boundOrientationChange && this.mediaQuery.removeEventListener) {
205629
+ this.mediaQuery.removeEventListener('change', this.boundOrientationChange);
205630
+ this.boundOrientationChange = null;
205631
+ }
205632
+ this.mediaQuery = null;
205255
205633
  }
205256
205634
  static { this.ɵfac = ɵɵngDeclareFactory$1({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0$1, type: Viewport, deps: [], target: FactoryTarget$4.Injectable }); }
205257
205635
  static { this.ɵprov = ɵɵngDeclareInjectable$1({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0$1, type: Viewport, providedIn: 'root' }); }
@@ -207905,7 +208283,10 @@ class PipeProvider {
207905
208283
  this.preparePipeMeta(CurrencyPipe$1, 'currency', true, [this._locale]),
207906
208284
  this.preparePipeMeta(DatePipe$1, 'date', true, [this._locale]),
207907
208285
  this.preparePipeMeta(ToDatePipe, 'toDate', true, [
207908
- new DatePipe$1(this._locale), undefined, this.injector.get(CustomPipeManager)
208286
+ new DatePipe$1(this._locale),
208287
+ undefined,
208288
+ this.injector.get(App),
208289
+ this.injector.get(CustomPipeManager)
207909
208290
  ]),
207910
208291
  this.preparePipeMeta(ToNumberPipe, 'toNumber', true, [
207911
208292
  new DecimalPipe$1(this._locale),
@@ -207922,7 +208303,7 @@ class PipeProvider {
207922
208303
  new DecimalPipe$1(this._locale)
207923
208304
  ]),
207924
208305
  this.preparePipeMeta(StringToNumberPipe, 'stringToNumber', true),
207925
- this.preparePipeMeta(CustomPipe, 'custom', true, [this.injector.get(CustomPipeManager)]),
208306
+ this.preparePipeMeta(CustomPipe, 'custom', true, [this.injector.get(App), this.injector.get(CustomPipeManager)]),
207926
208307
  this.preparePipeMeta(TrustAsPipe, 'trustAs', true, [this.domSanitizer]),
207927
208308
  this.preparePipeMeta(SanitizePipe, 'sanitize', true, [this.domSanitizer]),
207928
208309
  this.preparePipeMeta(TemplateReplacePipe, 'templateReplace', true),