PageSourceSearch

https://fraternitybandofficial.com.au/_content/Radzen.Blazor/Radzen.Blazor.js?v=11.4.3.0

js fraternitybandofficial.com.au collected 2026-09-25 10:57:29 UTC 290,616 bytes, 7,819 lines download raw bytes

1if (!Element.prototype.matches) {
2  Element.prototype.matches =
3    Element.prototype.msMatchesSelector ||
4    Element.prototype.webkitMatchesSelector;
5}
6
7if (!Element.prototype.closest) {
8  Element.prototype.closest = function (s) {
9    var el = this;
10
11    do {
12      if (el.matches(s)) return el;
13      el = el.parentElement || el.parentNode;
14    } while (el !== null && el.nodeType === 1);
15    return null;
16  };
17}
18
19// Suppress "DotNetObjectReference already disposed" promise rejections during component teardown.
20function suppressDisposed(promise) {
21  if (promise && typeof promise.catch === 'function') {
22    promise.catch(function () { });
23  }
24}
25window.addEventListener('unhandledrejection', function (e) {
26  if (e.reason && e.reason.message && e.reason.message.indexOf('DotNetObjectReference') !== -1) {
27    e.preventDefault();
28  }
29});
30
31var resolveCallbacks = [];
32var rejectCallbacks = [];
33var radzenRecognition;
34
35window.Radzen = {
36    isRTL: function (el) {
37        return el && getComputedStyle(el).direction == 'rtl';
38    },
39    throttle: function (callback, delay) {
40        var timeout = null;
41        return function () {
42            var args = arguments;
43            var ctx = this;
44            if (!timeout) {
45                timeout = setTimeout(function () {
46                    callback.apply(ctx, args);
47                    timeout = null;
48                }, delay);
49            }
50        };
51    },
52    downloadFile: async function (fileName, data, mimeType) {
53        const buffer = typeof data.arrayBuffer === 'function' ? await data.arrayBuffer() : data;
54        const blob = new Blob([buffer], { type: mimeType });
55        const url = URL.createObjectURL(blob);
56
57        const a = document.createElement("a");
58        a.href = url;
59        a.download = fileName;
60        a.click();
61
62        URL.revokeObjectURL(url);
63    },
64    mask: function (id, mask, pattern, characterPattern) {
65      var el = document.getElementById(id);
66      if (el) {
67          var format = function (value, mask, pattern, characterPattern) {
68              var chars = !characterPattern ? value.replace(new RegExp(pattern, "g"), "").split('') : value.match(new RegExp(characterPattern, "g"));
69              var count = 0;
70
71              var formatted = '';
72              for (var i = 0; i < mask.length; i++) {
73                  const c = mask[i];
74                  if (chars && chars[count]) {
75                      if (c === '*' || c == chars[count]) {
76                          formatted += chars[count];
77                          count++;
78                      } else {
79                          formatted += c;
80                      }
81                  }
82              }
83              return formatted;
84          }
85
86          if (window.safari !== undefined) {
87              el.onblur = function (e) {
88                  el.dispatchEvent(new Event('change'));
89              };
90          }
91
92          var start = el.selectionStart != el.value.length ? el.selectionStart : -1;
93          var end = el.selectionEnd != el.value.length ? el.selectionEnd : -1;
94
95          el.value = format(el.value, mask, pattern, characterPattern);
96
97          el.selectionStart = start != -1 ? start : el.selectionStart;
98          el.selectionEnd = end != -1 ? end : el.selectionEnd;
99      }
100  },
101  addContextMenu: function (id, ref) {
102     var el = document.getElementById(id);
103     if (el) {
104        var handler = function (e) {
105            e.stopPropagation();
106            e.preventDefault();
107            try {
108            ref.invokeMethodAsync('RadzenComponent.RaiseContextMenu',
109                {
110                    ClientX: e.clientX,
111                    ClientY: e.clientY,
112                    ScreenX: e.screenX,
113                    ScreenY: e.screenY,
114                    AltKey: e.altKey,
115                    ShiftKey: e.shiftKey,
116                    CtrlKey: e.ctrlKey,
117                    MetaKey: e.metaKey,
118                    Button: e.button,
119                    Buttons: e.buttons,
120                });
121            } catch { }
122            return false;
123        };
124        Radzen[id + 'contextmenu'] = handler;
125        el.addEventListener('contextmenu', handler, false);
126     }
127  },
128  addMouseEnter: function (id, ref) {
129     var el = document.getElementById(id);
130     if (el) {
131        var handler = function (e) {
132            try { suppressDisposed(ref.invokeMethodAsync('RadzenComponent.RaiseMouseEnter')); } catch { }
133        };
134        Radzen[id + 'mouseenter'] = handler;
135        el.addEventListener('mouseenter', handler, false);
136     }
137  },
138  addMouseLeave: function (id, ref) {
139     var el = document.getElementById(id);
140     if (el) {
141        var handler = function (e) {
142            try { suppressDisposed(ref.invokeMethodAsync('RadzenComponent.RaiseMouseLeave')); } catch { }
143        };
144        Radzen[id + 'mouseleave'] = handler;
145        el.addEventListener('mouseleave', handler, false);
146     }
147  },
148  removeContextMenu: function (id) {
149      var el = document.getElementById(id);
150      if (el && Radzen[id + 'contextmenu']) {
151          el.removeEventListener('contextmenu', Radzen[id + 'contextmenu']);
152      }
153  },
154  removeMouseEnter: function (id) {
155      var el = document.getElementById(id);
156      if (el && Radzen[id + 'mouseenter']) {
157          el.removeEventListener('mouseenter', Radzen[id + 'mouseenter']);
158      }
159  },
160  removeMouseLeave: function (id) {
161      var el = document.getElementById(id);
162      if (el && Radzen[id + 'mouseleave']) {
163          el.removeEventListener('mouseleave', Radzen[id + 'mouseleave']);
164      }
165  },
166  adjustDataGridHeader: function (scrollableHeader, scrollableBody) {
167    if (scrollableHeader && scrollableBody) {
168      scrollableHeader.style.cssText =
169        scrollableBody.clientHeight < scrollableBody.scrollHeight
170          ? 'margin-left:0px;padding-right: ' +
171            (scrollableBody.offsetWidth - scrollableBody.clientWidth) +
172            'px'
173          : 'margin-left:0px;';
174    }
175  },
176  ganttSyncScroll: function (ganttId) {
177    if (!ganttId) {
178      return;
179    }
180
181    var root = document.getElementById(ganttId);
182    if (!root) {
183      return;
184    }
185
186    var grid = root.querySelector('.rz-data-grid-data');
187    var timeline = root.querySelector('.rz-gantt-timeline-scroll');
188    if (!grid || !timeline) {
189      return;
190    }
191
192    var header = root.querySelector('.rz-gantt-table thead');
193    if (header) {
194      root.style.setProperty('--rz-gantt-header-height', header.getBoundingClientRect().height + 'px');
195    }
196
197    // Measure timeline group header and set CSS variable so the grid's
198    // thead::before pseudo-row matches it. Also sync overall header height.
199    function syncHeaderHeights() {
200      var gridThead = grid.querySelector('thead');
201      var timelineThead = timeline.querySelector('thead');
202      if (!gridThead || !timelineThead) return;
203
204      var groupRow = timelineThead.querySelector('.rz-gantt-header-group-row');
205      if (groupRow) {
206        root.style.setProperty('--rz-gantt-header-group-height', groupRow.offsetHeight + 'px');
207      } else {
208        root.style.setProperty('--rz-gantt-header-group-height', '0px');
209      }
210
211      var maxH = Math.max(gridThead.offsetHeight, timelineThead.offsetHeight);
212      root.style.setProperty('--rz-gantt-header-height', maxH + 'px');
213    }
214
215    function runSyncAfterLayout() {
216      requestAnimationFrame(function () {
217        requestAnimationFrame(syncHeaderHeights);
218      });
219    }
220
221    runSyncAfterLayout();
222
223    var key = ganttId + '_ganttScrollSync';
224    var state = Radzen[key];
225
226    if (state && state.grid === grid && state.timeline === timeline) {
227      return;
228    }
229
230    if (state) {
231      if (state.grid && state.onGrid) {
232        state.grid.removeEventListener('scroll', state.onGrid);
233      }
234      if (state.timeline && state.onTimeline) {
235        state.timeline.removeEventListener('scroll', state.onTimeline);
236      }
237      if (state.grid && state.onGridWheel) {
238        state.grid.removeEventListener('wheel', state.onGridWheel);
239      }
240    }
241
242    var syncing = false;
243    var onGrid = function () {
244      if (syncing) {
245        return;
246      }
247      syncing = true;
248      timeline.scrollTop = grid.scrollTop;
249      syncing = false;
250    };
251    var onTimeline = function () {
252      if (syncing) {
253        return;
254      }
255      syncing = true;
256      grid.scrollTop = timeline.scrollTop;
257      syncing = false;
258    };
259
260    // The grid has overflow-y: hidden (no native vertical scrollbar).
261    // Forward vertical mouse-wheel events to the timeline so the user
262    // can still scroll vertically while hovering over the grid.
263    var onGridWheel = function (e) {
264      if (e.shiftKey || e.deltaX) {
265        return;
266      }
267      if (e.deltaY) {
268        timeline.scrollTop += e.deltaY;
269        e.preventDefault();
270      }
271    };
272
273    grid.addEventListener('scroll', onGrid);
274    timeline.addEventListener('scroll', onTimeline);
275    grid.addEventListener('wheel', onGridWheel, { passive: false });
276
277    var resizeObserver = null;
278    if (typeof ResizeObserver !== 'undefined') {
279      resizeObserver = new ResizeObserver(runSyncAfterLayout);
280      resizeObserver.observe(root);
281    }
282
283    Radzen[key] = {
284      grid: grid,
285      timeline: timeline,
286      onGrid: onGrid,
287      onTimeline: onTimeline,
288      onGridWheel: onGridWheel,
289      resizeObserver: resizeObserver
290    };
291
292    timeline.scrollTop = grid.scrollTop;
293  },
294  ganttScrollToFirstEvent: function (ganttId) {
295    if (!ganttId) return false;
296    var root = document.getElementById(ganttId);
297    if (!root) return false;
298    var timeline = root.querySelector('.rz-gantt-timeline-scroll');
299    if (!timeline) return false;
300    var events = timeline.querySelectorAll('.rz-event');
301    if (!events.length) return false;
302
303    var timelineRect = timeline.getBoundingClientRect();
304    var thead = timeline.querySelector('thead');
305    var headerHeight = thead ? thead.offsetHeight : 0;
306
307    var bestTop = Infinity;
308    var bestLeft = Infinity;
309    for (var i = 0; i < events.length; i++) {
310      var rect = events[i].getBoundingClientRect();
311      if (rect.width <= 0) continue;
312      var contentTop = rect.top - timelineRect.top + timeline.scrollTop;
313      var contentLeft = rect.left - timelineRect.left + timeline.scrollLeft;
314      if (contentTop < bestTop || (contentTop === bestTop && contentLeft < bestLeft)) {
315        bestTop = contentTop;
316        bestLeft = contentLeft;
317      }
318    }
319    if (bestTop === Infinity) return false;
320
321    timeline.scrollTop = Math.max(0, bestTop - headerHeight - 4);
322    timeline.scrollLeft = Math.max(0, bestLeft - 40);
323    return true;
324  },
325  ganttSyncScrollDispose: function (ganttId) {
326    if (!ganttId) {
327      return;
328    }
329
330    var key = ganttId + '_ganttScrollSync';
331    var state = Radzen[key];
332    if (!state) {
333      return;
334    }
335
336    if (state.grid && state.onGrid) {
337      state.grid.removeEventListener('scroll', state.onGrid);
338    }
339    if (state.timeline && state.onTimeline) {
340      state.timeline.removeEventListener('scroll', state.onTimeline);
341    }
342    if (state.grid && state.onGridWheel) {
343      state.grid.removeEventListener('wheel', state.onGridWheel);
344    }
345    if (state.resizeObserver) {
346      state.resizeObserver.disconnect();
347    }
348
349    delete Radzen[key];
350  },
351  ganttInitDragResize: function (containerId, dotnetRef, timelineRangeMs, allowDrag, allowResize, rowHeightPx) {
352    var key = containerId + '_ganttDragResize';
353
354    if (Radzen[key]) {
355      Radzen[key].dotnetRef = dotnetRef;
356      Radzen[key].timelineRangeMs = timelineRangeMs;
357      Radzen[key].allowDrag = allowDrag;
358      Radzen[key].allowResize = allowResize;
359      Radzen[key].rowHeightPx = rowHeightPx || 36;
360      return;
361    }
362
363    var root = document.getElementById(containerId);
364    if (!root) return;
365
366    var container = root.querySelector('.rz-gantt-timeline-scroll');
367    if (!container) return;
368
369    var resizeThreshold = 6;
370    var dragThreshold = 3;
371    var drag = null;
372    var justDragged = false;
373
374    function getConfig() {
375      return Radzen[key];
376    }
377
378    function buildLinkPath(x1, x2, y1, y2, rh, linkType) {
379      var offset = 12;
380      var halfRow = rh * 0.5;
381      var exitRight = linkType === 0 || linkType === 2;
382      var enterRight = linkType === 2 || linkType === 3;
383      var exitDir = exitRight ? 1 : -1;
384      var enterDir = enterRight ? 1 : -1;
385      var exitX = x1 + offset * exitDir;
386      var enterX = x2 + offset * enterDir;
387      var canGoStraight = exitRight !== enterRight
388        ? (exitRight ? x2 - x1 > offset * 2 : x1 - x2 > offset * 2)
389        : (exitRight ? exitX < enterX : exitX > enterX);
390
391      if (canGoStraight && y1 !== y2) {
392        var midX = (exitX + enterX) / 2;
393        return 'M' + x1 + ',' + y1 + ' L' + midX + ',' + y1 + ' L' + midX + ',' + y2 + ' L' + x2 + ',' + y2;
394      }
395      var midY = y2 > y1 ? y1 + halfRow : (y2 < y1 ? y1 - halfRow : y1 + halfRow);
396      return 'M' + x1 + ',' + y1 + ' L' + exitX + ',' + y1 + ' L' + exitX + ',' + midY + ' L' + enterX + ',' + midY + ' L' + enterX + ',' + y2 + ' L' + x2 + ',' + y2;
397    }
398
399    function updateConnectedPaths() {
400      if (!drag) return;
401      var svg = container.querySelector('.rz-gantt-links');
402      if (!svg) return;
403
404      var config = getConfig();
405      var rh = config.rowHeightPx;
406      var svgWidth = svg.getBoundingClientRect().width;
407      var idx = drag.index;
408
409      var paths = svg.querySelectorAll('path[data-from-index="' + idx + '"], path[data-to-index="' + idx + '"]');
410      for (var i = 0; i < paths.length; i++) {
411        var path = paths[i];
412        var fromIdx = path.dataset.fromIndex;
413        var toIdx = path.dataset.toIndex;
414        var linkType = parseInt(path.dataset.linkType, 10) || 0;
415
416        var fromBar = container.querySelector('[data-index="' + fromIdx + '"]');
417        var toBar = container.querySelector('[data-index="' + toIdx + '"]');
418        if (!fromBar || !toBar) continue;
419
420        var fromRow = parseInt(fromBar.dataset.row, 10);
421        var toRow = parseInt(toBar.dataset.row, 10);
422
423        var fromLeft = parseFloat(fromBar.style.getPropertyValue('inset-inline-start')) || 0;
424        var fromWidth = parseFloat(fromBar.style.width) || 0;
425        var toLeft = parseFloat(toBar.style.getPropertyValue('inset-inline-start')) || 0;
426        var toWidth = parseFloat(toBar.style.width) || 0;
427        var fromIsMilestone = fromBar.classList.contains('rz-gantt-milestone');
428        var toIsMilestone = toBar.classList.contains('rz-gantt-milestone');
429
430        var x1, x2;
431        if (linkType === 1) { x1 = fromLeft; x2 = toLeft; }
432        else if (linkType === 2) { x1 = fromIsMilestone ? fromLeft : fromLeft + fromWidth; x2 = toIsMilestone ? toLeft : toLeft + toWidth; }
433        else if (linkType === 3) { x1 = fromLeft; x2 = toIsMilestone ? toLeft : toLeft + toWidth; }
434        else { x1 = fromIsMilestone ? fromLeft : fromLeft + fromWidth; x2 = toLeft; }
435
436        var x1px = (x1 / 100) * svgWidth;
437        var x2px = (x2 / 100) * svgWidth;
438        var y1 = (fromRow + 0.5) * rh;
439        var y2 = (toRow + 0.5) * rh;
440
441        path.setAttribute('d', buildLinkPath(x1px, x2px, y1, y2, rh, linkType));
442      }
443    }
444
445    function onMouseDown(e) {
446      var config = getConfig();
447      if (!config) return;
448
449      var bar = e.target.closest('.rz-gantt-bar, .rz-gantt-milestone');
450      if (!bar || e.button !== 0 || !bar.dataset.index) return;
451
452      var isMilestone = bar.classList.contains('rz-gantt-milestone');
453      var barRect = bar.getBoundingClientRect();
454      var offsetX = e.clientX - barRect.left;
455
456      var mode;
457      if (isMilestone) {
458        if (!config.allowDrag) return;
459        mode = 'move';
460      } else if (config.allowResize && offsetX <= resizeThreshold) {
461        mode = 'resize-start';
462      } else if (config.allowResize && barRect.width - offsetX <= resizeThreshold) {
463        mode = 'resize-end';
464      } else if (config.allowDrag) {
465        mode = 'move';
466      } else {
467        return;
468      }
469
470      e.preventDefault();
471
472      var containerEl = bar.closest('.rz-gantt-row-container');
473      var containerWidth = containerEl ? containerEl.getBoundingClientRect().width : 1;
474
475      drag = {
476        bar: bar,
477        mode: mode,
478        startClientX: e.clientX,
479        containerWidth: containerWidth,
480        originalLeft: parseFloat(bar.style.getPropertyValue('inset-inline-start')) || 0,
481        originalWidth: parseFloat(bar.style.width) || 0,
482        offsetStart: parseFloat(bar.dataset.offsetStart) || 0,
483        offsetEnd: parseFloat(bar.dataset.offsetEnd) || 0,
484        index: parseInt(bar.dataset.index, 10),
485        isMilestone: isMilestone,
486        hasMoved: false
487      };
488
489      document.addEventListener('mousemove', onMouseMove);
490      document.addEventListener('mouseup', onMouseUp);
491    }
492
493    function onMouseMove(e) {
494      if (!drag) return;
495
496      var deltaX = e.clientX - drag.startClientX;
497      if (!drag.hasMoved && Math.abs(deltaX) < dragThreshold) return;
498      drag.hasMoved = true;
499
500      var percentDelta = (deltaX / drag.containerWidth) * 100;
501
502      if (drag.mode === 'move') {
503        drag.bar.style.setProperty('inset-inline-start', (drag.originalLeft + percentDelta) + '%');
504      } else if (drag.mode === 'resize-start') {
505        var newWidth = drag.originalWidth - percentDelta;
506        if (newWidth > 0.1) {
507          drag.bar.style.setProperty('inset-inline-start', (drag.originalLeft + percentDelta) + '%');
508          drag.bar.style.width = newWidth + '%';
509        }
510      } else if (drag.mode === 'resize-end') {
511        var newWidth = drag.originalWidth + percentDelta;
512        if (newWidth > 0.1) {
513          drag.bar.style.width = newWidth + '%';
514        }
515      }
516
517      drag.bar.style.zIndex = '10';
518      drag.bar.style.opacity = '0.85';
519      document.body.style.cursor = drag.mode === 'move' ? 'grabbing' : 'ew-resize';
520
521      updateConnectedPaths();
522    }
523
524    function onMouseUp(e) {
525      if (!drag) return;
526
527      document.removeEventListener('mousemove', onMouseMove);
528      document.removeEventListener('mouseup', onMouseUp);
529
530      drag.bar.style.zIndex = '';
531      drag.bar.style.opacity = '';
532      document.body.style.cursor = '';
533
534      if (drag.hasMoved) {
535        justDragged = true;
536
537        var deltaX = e.clientX - drag.startClientX;
538        var percentDelta = (deltaX / drag.containerWidth) * 100;
539        var config = getConfig();
540        var msPerPercent = config.timelineRangeMs / 100;
541        var deltaMs = percentDelta * msPerPercent;
542
543        var newStartMs, newEndMs;
544        if (drag.mode === 'move') {
545          newStartMs = drag.offsetStart + deltaMs;
546          newEndMs = drag.offsetEnd + deltaMs;
547        } else if (drag.mode === 'resize-start') {
548          newStartMs = drag.offsetStart + deltaMs;
549          newEndMs = drag.offsetEnd;
550        } else {
551          newStartMs = drag.offsetStart;
552          newEndMs = drag.offsetEnd + deltaMs;
553        }
554
555        var eventType = drag.mode === 'move' ? 'move' : 'resize';
556        try { suppressDisposed(config.dotnetRef.invokeMethodAsync('OnGanttBarInteractionEnd', drag.index, eventType, newStartMs, newEndMs)); } catch { }
557      }
558
559      drag = null;
560    }
561
562    function onMouseMoveHover(e) {
563      if (drag) return;
564      var config = getConfig();
565      if (!config) return;
566
567      var bar = e.target.closest('.rz-gantt-bar, .rz-gantt-milestone');
568      if (!bar || !bar.dataset.index) return;
569
570      if (bar.classList.contains('rz-gantt-milestone')) {
571        bar.style.cursor = config.allowDrag ? 'grab' : '';
572        return;
573      }
574
575      var barRect = bar.getBoundingClientRect();
576      var offsetX = e.clientX - barRect.left;
577
578      if (config.allowResize && (offsetX <= resizeThreshold || barRect.width - offsetX <= resizeThreshold)) {
579        bar.style.cursor = 'ew-resize';
580      } else if (config.allowDrag) {
581        bar.style.cursor = 'grab';
582      }
583    }
584
585    function onClickCapture(e) {
586      if (justDragged) {
587        e.stopPropagation();
588        e.preventDefault();
589        justDragged = false;
590      }
591    }
592
593    container.addEventListener('mousedown', onMouseDown);
594    container.addEventListener('mousemove', onMouseMoveHover);
595    container.addEventListener('click', onClickCapture, true);
596
597    Radzen[key] = {
598      container: container,
599      dotnetRef: dotnetRef,
600      timelineRangeMs: timelineRangeMs,
601      allowDrag: allowDrag,
602      allowResize: allowResize,
603      rowHeightPx: rowHeightPx || 36,
604      onMouseDown: onMouseDown,
605      onMouseMoveHover: onMouseMoveHover,
606      onClickCapture: onClickCapture
607    };
608  },
609  ganttDisposeDragResize: function (containerId) {
610    var key = containerId + '_ganttDragResize';
611    var config = Radzen[key];
612    if (!config) return;
613
614    config.container.removeEventListener('mousedown', config.onMouseDown);
615    config.container.removeEventListener('mousemove', config.onMouseMoveHover);
616    config.container.removeEventListener('click', config.onClickCapture, true);
617    delete Radzen[key];
618  },
619  preventDefaultAndStopPropagation: function (e) {
620    e.preventDefault();
621    e.stopPropagation();
622  },
623  preventArrows: function (el) {
624    var preventDefault = function (e) {
625      if (e.keyCode === 38 || e.keyCode === 40) {
626        e.preventDefault();
627        return false;
628      }
629    };
630    if (el) {
631       el.addEventListener('keydown', preventDefault, false);
632    }
633  },
634  selectTab: function (id, index) {
635    var el = document.getElementById(id);
636    if (el && el.parentNode && el.parentNode.previousElementSibling) {
637        var tablist = el.parentNode.previousElementSibling;
638        var count = el.parentNode.children.length;
639        for (var i = 0; i < count; i++) {
640            var content = el.parentNode.children[i];
641            if (content) {
642                content.style.display = i == index ? '' : 'none';
643                content.setAttribute('aria-hidden', i == index ? 'false' : 'true');
644            }
645            var header = tablist.children[i];
646            if (header) {
647                var btn = header.querySelector('[role="tab"]') || header;
648                if (i == index) {
649                    header.classList.add('rz-tabview-selected');
650                    header.classList.add('rz-state-focused');
651                    btn.setAttribute('aria-selected', 'true');
652                }
653                else {
654                    header.classList.remove('rz-tabview-selected');
655                    header.classList.remove('rz-state-focused');
656                    btn.setAttribute('aria-selected', 'false');
657                }
658            }
659        }
660        if (tablist.getAttribute && tablist.getAttribute('role') === 'tablist') {
661            var activeBtn = tablist.children[index] && (tablist.children[index].querySelector('[role="tab"]') || tablist.children[index]);
662            if (activeBtn && activeBtn.id) {
663                tablist.setAttribute('aria-activedescendant', activeBtn.id);
664            }
665        }
666    }
667  },
668  createAccordion: function (el, multiple) {
669    function getItems() {
670        var headers = [];
671        var expanders = [];
672        for (var i = 0; i < el.children.length; i++) {
673            var child = el.children[i];
674            if (child.classList.contains('rz-accordion-header')) {
675                headers.push(child);
676            } else if (child.classList.contains('rz-expander')) {
677                expanders.push(child);
678            }
679        }
680        return { headers: headers, expanders: expanders };
681    }
682
683    function toggleItem(index, expanded) {
684        var items = getItems();
685        if (index < 0 || index >= items.headers.length) return;
686
687        if (!multiple) {
688            for (var i = 0; i < items.headers.length; i++) {
689                if (i !== index) {
690                    var btn = items.headers[i].querySelector('button');
691                    var icon = items.headers[i].querySelector('.rz-accordion-toggle-icon');
692                    if (btn) btn.setAttribute('aria-expanded', 'false');
693                    if (icon) {
694                        icon.classList.remove('rz-state-expanded');
695                        icon.classList.add('rz-state-collapsed');
696                    }
697                    items.expanders[i].classList.remove('rz-state-expanded');
698                    items.expanders[i].classList.add('rz-state-collapsed');
699                    items.expanders[i].setAttribute('aria-hidden', 'true');
700                }
701            }
702        }
703
704        var button = items.headers[index].querySelector('button');
705        var toggleIcon = items.headers[index].querySelector('.rz-accordion-toggle-icon');
706        if (button) button.setAttribute('aria-expanded', expanded ? 'true' : 'false');
707        if (toggleIcon) {
708            toggleIcon.classList.remove(expanded ? 'rz-state-collapsed' : 'rz-state-expanded');
709            toggleIcon.classList.add(expanded ? 'rz-state-expanded' : 'rz-state-collapsed');
710        }
711        items.expanders[index].classList.remove(expanded ? 'rz-state-collapsed' : 'rz-state-expanded');
712        items.expanders[index].classList.add(expanded ? 'rz-state-expanded' : 'rz-state-collapsed');
713        items.expanders[index].setAttribute('aria-hidden', expanded ? 'false' : 'true');
714    }
715
716    function onKeyDown(e) {
717        var key = e.code || e.key;
718        if (key !== 'ArrowUp' && key !== 'ArrowDown' && key !== 'Home' && key !== 'End') {
719            return;
720        }
721        var target = e.target;
722        if (target && target.closest && target.closest('.rz-accordion-header')) {
723            e.preventDefault();
724        }
725    }
726
727    el.addEventListener('keydown', onKeyDown);
728
729    return {
730        toggle: function (index, expanded) { toggleItem(index, expanded); },
731        setMultiple: function (value) { multiple = value; },
732        dispose: function () { el.removeEventListener('keydown', onKeyDown); }
733    };
734  },
735  loadGoogleMaps: function (defaultView, apiKey, resolve, reject, language) {
736    resolveCallbacks.push(resolve);
737    rejectCallbacks.push(reject);
738
739    if (defaultView['rz_map_init']) {
740      return;
741    }
742
743    defaultView['rz_map_init'] = function () {
744      for (var i = 0; i < resolveCallbacks.length; i++) {
745        resolveCallbacks[i](defaultView.google);
746      }
747    };
748
749    var document = defaultView.document;
750    var script = document.createElement('script');
751
752    script.src =
753      'https://maps.googleapis.com/maps/api/js?' +
754      (language ? 'language=' + language + '&' : '') +
755      (apiKey ? 'key=' + apiKey + '&' : '') +
756      'callback=rz_map_init&libraries=marker';
757
758    script.async = true;
759    script.defer = true;
760    script.onerror = function (err) {
761      for (var i = 0; i < rejectCallbacks.length; i++) {
762        rejectCallbacks[i](err);
763      }
764    };
765
766    document.body.appendChild(script);
767  },
768  createMap: function (wrapper, ref, id, apiKey, mapId, zoom, center, markers, options, fitBoundsToMarkersOnUpdate, language) {
769    var api = function () {
770      var defaultView = document.defaultView;
771
772      return new Promise(function (resolve, reject) {
773        if (defaultView.google && defaultView.google.maps) {
774          return resolve(defaultView.google);
775        }
776
777        Radzen.loadGoogleMaps(defaultView, apiKey, resolve, reject, language);
778      });
779    };
780
781    api().then(function (google) {
782      Radzen[id] = ref;
783      Radzen[id].google = google;
784
785      Radzen[id].instance = new google.maps.Map(wrapper, {
786        center: center,
787        zoom: zoom,
788        mapId: mapId
789      });
790
791      Radzen[id].instance.addListener('click', function (e) {
792        try { Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMapClick', {
793          Position: {Lat: e.latLng.lat(), Lng: e.latLng.lng()}
794        }); } catch { }
795      });
796
797      Radzen.updateMap(id, apiKey, zoom, center, markers, options, fitBoundsToMarkersOnUpdate, language);
798    });
799
800    return {
801      dispose: function () {
802        if (Radzen[id] && Radzen[id].instance) {
803          delete Radzen[id].instance;
804        }
805      }
806    };
807  },
808  updateMap: function (id, apiKey, zoom, center, markers, options, fitBoundsToMarkersOnUpdate, language) {
809    var api = function () {
810        var defaultView = document.defaultView;
811
812        return new Promise(function (resolve, reject) {
813            if (defaultView.google && defaultView.google.maps) {
814                return resolve(defaultView.google);
815            }
816
817            Radzen.loadGoogleMaps(defaultView, apiKey, resolve, reject, language);
818        });
819    };
820    api().then(function (google) {
821        let markerBounds = new google.maps.LatLngBounds();
822
823        if (Radzen[id] && Radzen[id].instance) {
824            if (Radzen[id].instance.markers && Radzen[id].instance.markers.length) {
825                for (var i = 0; i < Radzen[id].instance.markers.length; i++) {
826                    Radzen[id].instance.markers[i].setMap(null);
827                }
828            }
829
830            if (markers) {
831                Radzen[id].instance.markers = [];
832
833                markers.forEach(function (m) {
834                    var content;
835
836                    if (m.label) {
837                        content = document.createElement('span');
838                        content.innerHTML = m.label;
839                    }
840
841                    var marker = new this.google.maps.marker.AdvancedMarkerElement({
842                        position: m.position,
843                        title: m.title,
844                        content: content
845                    });
846
847                    marker.addListener('click', function (e) {
848                        try { Radzen[id].invokeMethodAsync('RadzenGoogleMap.OnMarkerClick', {
849                            Title: marker.title,
850                            Label: marker.content.innerText,
851                            Position: marker.position
852                        }); } catch { }
853                    });
854
855                    marker.setMap(Radzen[id].instance);
856
857                    Radzen[id].instance.markers.push(marker);
858
859                        markerBounds.extend(marker.position);
860                });
861            }
862
863            if (zoom) {
864                Radzen[id].instance.setZoom(zoom);
865                }
866
867            if (center) {
868                Radzen[id].instance.setCenter(center);
869            }
870
871            if (options) {
872                Radzen[id].instance.setOptions(options);
873            }
874
875            if (markers && fitBoundsToMarkersOnUpdate) {
876                Radzen[id].instance.fitBounds(markerBounds);
877            }
878        }
879    });
880  },
881 focusSecurityCode: function (el) {
882    if (!el) return;
883    var firstInput = el.querySelector('.rz-security-code-input');
884    if (firstInput) {
885        setTimeout(function () { firstInput.focus() }, 500);
886    }
887 },
888  createSecurityCode: function (id, ref, el, isNumber) {
889      if (!el || !ref) return { dispose: function() {} };
890
891      var hidden = el.querySelector('input[type="hidden"]');
892
893      var state = {};
894      Radzen[id] = state;
895
896      state.inputs = [...el.querySelectorAll('.rz-security-code-input')];
897
898      var isAndroid = navigator.userAgent.match(/Android/i);
899
900      state.paste = function (e) {
901          if (e.clipboardData) {
902              var value = e.clipboardData.getData('text');
903
904              if (value) {
905                  for (var i = 0; i < Math.min(value.length, state.inputs.length); i++) {
906                      if (isNumber && isNaN(+value[i])) {
907                          continue;
908                      }
909                      state.inputs[i].value = value[i];
910                  }
911
912                  var code = state.inputs.map(i => i.value).join('').trim();
913                  hidden.value = code;
914
915                  try { suppressDisposed(ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', code)); } catch { }
916
917                  state.inputs[state.inputs.length - 1].focus();
918              }
919
920              e.preventDefault();
921          }
922      }
923      state.keyPress = function (e) {
924          var keyCode = e.data ? e.data.charCodeAt(0) : e.which;
925          var ch = e.data || String.fromCharCode(e.which);
926
927          if (e.metaKey ||
928              e.ctrlKey ||
929              keyCode == 9 ||
930              keyCode == 8 ||
931              keyCode == 13
932          ) {
933              return;
934          }
935
936          if (isNumber && (keyCode < 48 || keyCode > 57)) {
937              e.preventDefault();
938              return;
939          }
940
941          // Android-specific: prevent default to avoid double input
942          if (isAndroid && e.type === 'textInput') {
943              e.preventDefault();
944          }
945
946          if (e.currentTarget.value == ch) {
947              return;
948          }
949
950          e.currentTarget.value = ch;
951
952          var value = state.inputs.map(i => i.value).join('').trim();
953          hidden.value = value;
954
955          try { suppressDisposed(ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value)); } catch { }
956
957          var index = state.inputs.indexOf(e.currentTarget);
958          if (index < state.inputs.length - 1) {
959              state.inputs[index + 1].focus();
960          }
961      }
962
963      state.keyDown = function (e) {
964          var keyCode = e.which || e.keyCode;
965          if (keyCode == 8) {
966              e.currentTarget.value = '';
967
968              var value = state.inputs.map(i => i.value).join('').trim();
969              hidden.value = value;
970
971              try { suppressDisposed(ref.invokeMethodAsync('RadzenSecurityCode.OnValueChange', value)); } catch { }
972
973              var index = state.inputs.indexOf(e.currentTarget);
974              if (index > 0) {
975                  state.inputs[index - 1].focus();
976              }
977          }
978      }
979
980      for (var i = 0; i < state.inputs.length; i++) {
981          state.inputs[i].addEventListener(isAndroid ? 'textInput' : 'keypress', state.keyPress);
982          state.inputs[i].addEventListener('keydown', state.keyDown);
983          state.inputs[i].addEventListener('paste', state.paste);
984      }
985
986      return {
987          dispose: function () {
988              for (var i = 0; i < state.inputs.length; i++) {
989                  state.inputs[i].removeEventListener(isAndroid ? 'textInput' : 'keypress', state.keyPress);
990                  state.inputs[i].removeEventListener('keydown', state.keyDown);
991                  state.inputs[i].removeEventListener('paste', state.paste);
992              }
993
994              if (Radzen[id] === state) {
995                  Radzen[id] = null;
996              }
997          }
998      };
999  },
1000  createSlider: function (
1001    id,
1002    slider,
1003    parent,
1004    range,
1005    minHandle,
1006    maxHandle,
1007    min,
1008    max,
1009    value,
1010    step,
1011    isVertical
1012  ) {
1013    var state = {};
1014    Radzen[id] = state;
1015    state.mouseMoveHandler = function (e) {
1016      e.preventDefault();
1017
1018      var handle = slider.isMin ? minHandle : maxHandle;
1019
1020      if (!slider.canChange) return;
1021
1022      var offsetX =
1023        e.targetTouches && e.targetTouches[0]
1024          ? e.targetTouches[0].pageX - e.target.getBoundingClientRect().left
1025                : e.pageX - handle.getBoundingClientRect().left;
1026
1027      var offsetY =
1028        e.targetTouches && e.targetTouches[0]
1029          ? e.targetTouches[0].pageY - e.target.getBoundingClientRect().top
1030              : e.pageY - handle.getBoundingClientRect().top;
1031
1032      var percent = isVertical ? (parent.offsetHeight - handle.offsetTop - offsetY) / parent.offsetHeight
1033        : (Radzen.isRTL(handle) ? parent.offsetWidth - handle.offsetLeft - offsetX : handle.offsetLeft + offsetX) / parent.offsetWidth;
1034
1035      if (percent > 1) {
1036        percent = 1;
1037      } else if (percent < 0) {
1038        percent = 0;
1039      }
1040
1041     var newValue = percent * (max - min) + min;
1042
1043      if (
1044        slider.canChange &&
1045        newValue >= min &&
1046        newValue <= max
1047      ) {
1048        try { slider.invokeMethodAsync(
1049          'RadzenSlider.OnValueChange',
1050          newValue,
1051          !!slider.isMin
1052        ); } catch { }
1053      }
1054    };
1055
1056    state.mouseDownHandler = function (e) {
1057      if (parent.classList.contains('rz-state-disabled')) return;
1058
1059      document.addEventListener('mousemove', state.mouseMoveHandler);
1060      document.addEventListener('touchmove', state.mouseMoveHandler, {
1061        passive: false, capture: true
1062      });
1063
1064      document.addEventListener('mouseup', state.mouseUpHandler);
1065      document.addEventListener('touchend', state.mouseUpHandler, {
1066        passive: true
1067      });
1068
1069      if (minHandle == e.target || maxHandle == e.target) {
1070        slider.canChange = true;
1071        slider.isMin = minHandle == e.target;
1072      } else {
1073
1074        var offsetX =
1075          e.targetTouches && e.targetTouches[0]
1076            ? e.targetTouches[0].pageX - e.target.getBoundingClientRect().left
1077                  : e.offsetX;
1078
1079        var percent = offsetX / parent.offsetWidth;
1080
1081        var newValue = percent * (max - min) + min;
1082        var oldValue = range ? value[slider.isMin ? 0 : 1] : value;
1083        if (newValue >= min && newValue <= max && newValue != oldValue) {
1084          try { slider.invokeMethodAsync(
1085            'RadzenSlider.OnValueChange',
1086            newValue,
1087            !!slider.isMin
1088          ); } catch { }
1089        }
1090      }
1091    };
1092
1093    state.mouseUpHandler = function (e) {
1094      slider.canChange = false;
1095      document.removeEventListener('mousemove', state.mouseMoveHandler);
1096      document.removeEventListener('touchmove', state.mouseMoveHandler, {
1097        passive: false, capture: true
1098      });
1099      document.removeEventListener('mouseup', state.mouseUpHandler);
1100      document.removeEventListener('touchend', state.mouseUpHandler, {
1101        passive: true
1102      });
1103    };
1104
1105    parent.addEventListener('mousedown', state.mouseDownHandler);
1106    parent.addEventListener('touchstart', state.mouseDownHandler, {
1107      passive: true
1108    });
1109
1110    return { dispose: function() {
1111      document.removeEventListener('mousemove', state.mouseMoveHandler);
1112      document.removeEventListener('touchmove', state.mouseMoveHandler, {
1113        passive: false, capture: true
1114      });
1115      document.removeEventListener('mouseup', state.mouseUpHandler);
1116      document.removeEventListener('touchend', state.mouseUpHandler, {
1117        passive: true
1118      });
1119      parent.removeEventListener('mousedown', state.mouseDownHandler);
1120      parent.removeEventListener('touchstart', state.mouseDownHandler, {
1121        passive: true
1122      });
1123
1124      if (Radzen[id] === state) {
1125        Radzen[id] = null;
1126      }
1127    }};
1128  },
1129  prepareDrag: function (el) {
1130    if (el) {
1131        el.ondragover = function (e) { e.preventDefault(); };
1132        el.ondragstart = function (e) { e.dataTransfer.setData('', e.target.id); };
1133    }
1134  },
1135  focusElement: function (elementId, onlyIfFocusNotMoved) {
1136    var el = document.getElementById(elementId);
1137    if (!el) return;
1138    if (onlyIfFocusNotMoved) {
1139      var ae = document.activeElement;
1140      if (ae && ae != document.body && ae != el && !ae.closest('.rz-lookup-panel, .rz-dropdown-panel, .rz-multiselect-panel')) {
1141        return;
1142      }
1143    }
1144    el.focus();
1145  },
1146  focusNext: function (container, reverse) {
1147    if (!container) return;
1148    var selector = 'a[href]:not([disabled]):not([tabindex="-1"]), button:not([disabled]):not([tabindex="-1"]), input:not([disabled]):not([type="hidden"]):not([tabindex="-1"]), select:not([disabled]):not([tabindex="-1"]), textarea:not([disabled]):not([tabindex="-1"]), [tabindex]:not([tabindex="-1"]):not([disabled])';
1149    var candidates = Array.from(document.querySelectorAll(selector)).filter(function (el) {
1150      return (el.offsetWidth > 0 || el.offsetHeight > 0) && el !== container && !container.contains(el);
1151    });
1152    if (reverse) {
1153      for (var i = candidates.length - 1; i >= 0; i--) {
1154        if (container.compareDocumentPosition(candidates[i]) & Node.DOCUMENT_POSITION_PRECEDING) {
1155          candidates[i].focus();
1156          return;
1157        }
1158      }
1159    } else {
1160      for (var i = 0; i < candidates.length; i++) {
1161        if (container.compareDocumentPosition(candidates[i]) & Node.DOCUMENT_POSITION_FOLLOWING) {
1162          candidates[i].focus();
1163          return;
1164        }
1165      }
1166    }
1167  },
1168  scrollCarouselItem: function (el, duration) {
1169    var container = el.parentElement;
1170    var target = el.offsetLeft;
1171    if (duration == null || duration < 0) {
1172        container.scroll(target, 0);
1173        return;
1174    }
1175    if (duration === 0) {
1176        container.style.scrollBehavior = 'auto';
1177        container.scroll(target, 0);
1178        container.style.scrollBehavior = '';
1179        return;
1180    }
1181    container.style.scrollBehavior = 'auto';
1182    container.style.scrollSnapType = 'none';
1183    var start = container.scrollLeft;
1184    var distance = target - start;
1185    var startTime = null;
1186    function step(timestamp) {
1187        if (!startTime) startTime = timestamp;
1188        var elapsed = timestamp - startTime;
1189        var progress = Math.min(elapsed / duration, 1);
1190        var ease = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;
1191        container.scrollLeft = start + distance * ease;
1192        if (elapsed < duration) {
1193            requestAnimationFrame(step);
1194        } else {
1195            container.style.scrollBehavior = '';
1196            container.style.scrollSnapType = '';
1197        }
1198    }
1199    requestAnimationFrame(step);
1200  },
1201  createCarousel: function (container, ref) {
1202    var scrollTimeout = null;
1203    function handler() {
1204        if (scrollTimeout) clearTimeout(scrollTimeout);
1205        scrollTimeout = setTimeout(function () {
1206            var children = container.children;
1207            if (!children.length) return;
1208            var itemWidth = children[0].offsetWidth;
1209            if (itemWidth === 0) return;
1210            var index = Math.round(container.scrollLeft / itemWidth);
1211            if (index < 0) index = 0;
1212            if (index >= children.length) index = children.length - 1;
1213            try { ref.invokeMethodAsync('RadzenCarousel.OnScroll', index); } catch { }
1214        }, 100);
1215    }
1216    container.addEventListener('scroll', handler, { passive: true });
1217    return { dispose: function () { container.removeEventListener('scroll', handler); } };
1218  },
1219  scrollIntoViewIfNeeded: function (ref, selector) {
1220    var el = selector ? ref.getElementsByClassName(selector)[0] : ref;
1221    if (el && el.scrollIntoViewIfNeeded) {
1222        el.scrollIntoViewIfNeeded();
1223    } else if (el && el.scrollIntoView) {
1224        el.scrollIntoView();
1225    }
1226  },
1227  scrollIntoViewIfNeededById: function (id) {
1228    var el = document.getElementById(id);
1229    if (el) {
1230        Radzen.scrollIntoViewIfNeeded(el);
1231    }
1232  },
1233  focusPanelMenuItem: function (menu, activeId) {
1234    if (!menu) return;
1235    var focused = menu.querySelectorAll('.rz-navigation-item.rz-state-focused');
1236    for (var i = 0; i < focused.length; i++) {
1237        focused[i].classList.remove('rz-state-focused');
1238    }
1239    if (activeId) {
1240        menu.setAttribute('aria-activedescendant', activeId);
1241        var el = document.getElementById(activeId);
1242        if (el) {
1243            el.classList.add('rz-state-focused');
1244            Radzen.scrollIntoViewIfNeeded(el);
1245        }
1246    } else {
1247        menu.removeAttribute('aria-activedescendant');
1248    }
1249  },
1250  updateActiveDescendant: function (ul, li, index) {
1251    if (!ul) return;
1252    // The popup is appended to document.body when opened, so ul.closest('[role="combobox"]')
1253    // fails once the dropdown is open. Fall back to a reverse lookup via aria-controls.
1254    // There can be more than one combobox referencing the same listbox (the trigger and a
1255    // filter input inside the popup). aria-activedescendant must be set only on the element
1256    // that currently has DOM focus (the filter input while filtering, otherwise the trigger),
1257    // so a non-focused combobox never carries a stale active-descendant.
1258    var comboboxes = [];
1259    var ancestor = ul.closest('[role="combobox"]');
1260    if (ancestor) {
1261      comboboxes.push(ancestor);
1262    }
1263    if (ul.id && window.CSS && CSS.escape) {
1264      var escapedId = CSS.escape(ul.id);
1265      var matches = document.querySelectorAll(
1266        '[aria-controls="' + escapedId + '"][role="combobox"]'
1267      );
1268      for (var i = 0; i < matches.length; i++) {
1269        if (comboboxes.indexOf(matches[i]) === -1) {
1270          comboboxes.push(matches[i]);
1271        }
1272      }
1273    }
1274
1275    var focused = null;
1276    for (var f = 0; f < comboboxes.length; f++) {
1277      if (comboboxes[f] === document.activeElement) {
1278        focused = comboboxes[f];
1279        break;
1280      }
1281    }
1282    if (!focused && comboboxes.length) {
1283      focused = comboboxes[0];
1284    }
1285
1286    for (var c = 0; c < comboboxes.length; c++) {
1287      if (comboboxes[c] !== focused) {
1288        comboboxes[c].removeAttribute('aria-activedescendant');
1289      }
1290    }
1291
1292    if (li && focused) {
1293      var itemId = ul.id + '-' + index;
1294      li.id = itemId;
1295      focused.setAttribute('aria-activedescendant', itemId);
1296    } else if (focused) {
1297      focused.removeAttribute('aria-activedescendant');
1298    }
1299  },
1300  selectListItem: function (input, ul, index) {
1301    if (!ul) return;
1302
1303    var childNodes = ul.getElementsByTagName('LI');
1304
1305    var highlighted = ul.querySelectorAll('.rz-state-highlight');
1306    if (highlighted.length) {
1307      for (var i = 0; i < highlighted.length; i++) {
1308        highlighted[i].classList.remove('rz-state-highlight');
1309      }
1310    }
1311
1312    ul.nextSelectedIndex = index;
1313
1314    if (
1315      ul.nextSelectedIndex >= 0 &&
1316      ul.nextSelectedIndex <= childNodes.length - 1
1317    ) {
1318      childNodes[ul.nextSelectedIndex].classList.add('rz-state-highlight');
1319      childNodes[ul.nextSelectedIndex].scrollIntoView({block:'nearest'});
1320      Radzen.updateActiveDescendant(ul, childNodes[ul.nextSelectedIndex], ul.nextSelectedIndex);
1321    } else {
1322      Radzen.updateActiveDescendant(ul, null, -1);
1323    }
1324  },
1325  focusListItem: function (input, ul, isDown, startIndex) {
1326    if (!ul) return;
1327    var childNodes = ul.getElementsByTagName('LI');
1328
1329    if (!childNodes || childNodes.length == 0) return;
1330
1331    if (startIndex == undefined || startIndex == null) {
1332      startIndex = -1;
1333    }
1334
1335    ul.nextSelectedIndex = startIndex;
1336    if (isDown) {
1337        while (ul.nextSelectedIndex < childNodes.length - 1) {
1338            ul.nextSelectedIndex++;
1339            if (!childNodes[ul.nextSelectedIndex].classList.contains('rz-state-disabled'))
1340                break;
1341        }
1342    } else {
1343        while (ul.nextSelectedIndex >= 0) {
1344            ul.nextSelectedIndex--;
1345            if (!childNodes[ul.nextSelectedIndex] || !childNodes[ul.nextSelectedIndex].classList.contains('rz-state-disabled'))
1346                break;
1347        }
1348    }
1349
1350    var highlighted = ul.querySelectorAll('.rz-state-highlight');
1351    if (highlighted.length) {
1352      for (var i = 0; i < highlighted.length; i++) {
1353        highlighted[i].classList.remove('rz-state-highlight');
1354      }
1355    }
1356
1357    if (
1358      ul.nextSelectedIndex >= 0 &&
1359      ul.nextSelectedIndex <= childNodes.length - 1
1360    ) {
1361      childNodes[ul.nextSelectedIndex].classList.add('rz-state-highlight');
1362      Radzen.scrollIntoViewIfNeeded(childNodes[ul.nextSelectedIndex]);
1363      Radzen.updateActiveDescendant(ul, childNodes[ul.nextSelectedIndex], ul.nextSelectedIndex);
1364    } else {
1365      Radzen.updateActiveDescendant(ul, null, -1);
1366    }
1367
1368    return ul.nextSelectedIndex;
1369  },
1370  focusVirtualListItem: function (ul, absoluteIndex, totalCount) {
1371    if (!ul) return;
1372
1373    var childNodes = ul.getElementsByTagName('LI');
1374    if (!childNodes || childNodes.length == 0) return;
1375
1376    var itemHeight = childNodes[0].offsetHeight;
1377    if (itemHeight <= 0) return;
1378
1379    var wrapper = ul.parentElement;
1380    if (!wrapper) return;
1381
1382    // Scroll to make the item at absoluteIndex visible
1383    if (absoluteIndex >= 0 && absoluteIndex < totalCount) {
1384      var targetTop = absoluteIndex * itemHeight;
1385      var targetBottom = targetTop + itemHeight;
1386      var viewTop = wrapper.scrollTop;
1387      var viewBottom = viewTop + wrapper.clientHeight;
1388
1389      if (targetBottom > viewBottom) {
1390        wrapper.scrollTop = targetBottom - wrapper.clientHeight;
1391      } else if (targetTop < viewTop) {
1392        wrapper.scrollTop = targetTop;
1393      }
1394    }
1395
1396    // Highlight the correct item after scroll
1397    function highlightItem() {
1398      var lis = ul.getElementsByTagName('LI');
1399      if (!lis || lis.length == 0) return;
1400
1401      var highlighted = ul.querySelectorAll('.rz-state-highlight');
1402      for (var i = 0; i < highlighted.length; i++) {
1403        highlighted[i].classList.remove('rz-state-highlight');
1404      }
1405
1406      // Determine the start index of rendered items from the top spacer
1407      var startIndex = 0;
1408      var spacer = ul.firstElementChild;
1409      if (spacer && spacer.tagName !== 'LI' && spacer.style && spacer.style.height) {
1410        startIndex = Math.round(parseFloat(spacer.style.height) / itemHeight);
1411      }
1412
1413      var relativeIndex = absoluteIndex - startIndex;
1414      if (relativeIndex >= 0 && relativeIndex < lis.length) {
1415        lis[relativeIndex].classList.add('rz-state-highlight');
1416        lis[relativeIndex].scrollIntoView({ block: 'nearest' });
1417        Radzen.updateActiveDescendant(ul, lis[relativeIndex], absoluteIndex);
1418      } else {
1419        Radzen.updateActiveDescendant(ul, null, -1);
1420      }
1421    }
1422
1423    // Use MutationObserver to wait for Virtualize to re-render after scroll
1424    var observer = new MutationObserver(function () {
1425      observer.disconnect();
1426      highlightItem();
1427    });
1428    observer.observe(ul, { childList: true, subtree: true });
1429
1430    // Also highlight immediately in case no DOM change is needed
1431    highlightItem();
1432
1433    // Disconnect observer after a timeout to avoid memory leaks
1434    setTimeout(function () { observer.disconnect(); }, 500);
1435  },
1436  clearFocusedHeaderCell: function (gridId) {
1437    var grid = document.getElementById(gridId);
1438    if (!grid) return;
1439
1440    var table = grid.querySelector('.rz-grid-table');
1441    var thead = table.getElementsByTagName("thead")[0];
1442    var highlightedCells = thead.querySelectorAll('.rz-state-focused');
1443    if (highlightedCells.length) {
1444        for (var i = 0; i < highlightedCells.length; i++) {
1445            highlightedCells[i].classList.remove('rz-state-focused');
1446        }
1447    }
1448  },
1449  focusTableRow: function (gridId, key, rowIndex, cellIndex, isVirtual) {
1450    var grid = document.getElementById(gridId);
1451    if (!grid) return;
1452
1453    var table = grid.querySelector('.rz-grid-table');
1454    var tbody = table.tBodies[0];
1455    var thead = table.tHead;
1456
1457    var rows = (cellIndex != null && thead && thead.rows && thead.rows.length ? [...thead.rows] : []).c
1457oncat(tbody && tbody.rows && tbody.rows.length ? [...tbody.rows] : []);
1458
1459    if (isVirtual && (key == 'ArrowUp' || key == 'ArrowDown' || key == 'PageUp' || key == 'PageDown' || key == 'Home' || key == 'End')) {
1460        if (rowIndex == 0 && (key == 'End' || key == 'PageDown')) {
1461            var highlightedCells = thead.querySelectorAll('.rz-state-focused');
1462            if (highlightedCells.length) {
1463                for (var i = 0; i < highlightedCells.length; i++) {
1464                    highlightedCells[i].classList.remove('rz-state-focused');
1465                }
1466            }
1467        }
1468        if (key == 'PageUp' || key == 'PageDown') {
1469            var rowHeight = rows[rows.length - 1] ? rows[rows.length - 1].offsetHeight : 40;
1470            var factor = 10;
1471            table.parentNode.scrollTop = table.parentNode.scrollTop + (factor * (key == 'PageDown' ? rowHeight : -rowHeight));
1472        }
1473        else if (key == 'Home' || key == 'End') {
1474            table.parentNode.scrollTop = key == 'Home' ? 0 : table.parentNode.scrollHeight;
1475        }
1476    }
1477
1478    table.nextSelectedIndex = rowIndex || 0;
1479    table.nextSelectedCellIndex = cellIndex || 0;
1480
1481    if (key == 'ArrowDown') {
1482        while (table.nextSelectedIndex < rows.length - 1) {
1483            table.nextSelectedIndex++;
1484            if (!rows[table.nextSelectedIndex] || !rows[table.nextSelectedIndex].classList.contains('rz-state-disabled'))
1485                break;
1486        }
1487    } else if (key == 'ArrowUp') {
1488        while (table.nextSelectedIndex > 0) {
1489            table.nextSelectedIndex--;
1490            if (!rows[table.nextSelectedIndex] || !rows[table.nextSelectedIndex].classList.contains('rz-state-disabled'))
1491                break;
1492        }
1493    } else if (key == 'ArrowRight') {
1494        while (table.nextSelectedCellIndex < rows[table.nextSelectedIndex].cells.length - 1) {
1495            table.nextSelectedCellIndex++;
1496            if (!rows[table.nextSelectedIndex] || !rows[table.nextSelectedIndex].cells[table.nextSelectedCellIndex] || !rows[table.nextSelectedIndex].cells[table.nextSelectedCellIndex].classList.contains('rz-state-disabled'))
1497                break;
1498        }
1499    } else if (key == 'ArrowLeft') {
1500        while (table.nextSelectedCellIndex > 0) {
1501            table.nextSelectedCellIndex--;
1502            if (!rows[table.nextSelectedIndex] || !rows[table.nextSelectedIndex].cells[table.nextSelectedCellIndex] || !rows[table.nextSelectedIndex].cells[table.nextSelectedCellIndex].classList.contains('rz-state-disabled'))
1503                break;
1504        }
1505    } else if (isVirtual && (key == 'PageDown' || key == 'End')) {
1506        table.nextSelectedIndex = rows.length - 1;
1507    } else if (isVirtual && (key == 'PageUp' || key == 'Home')) {
1508        table.nextSelectedIndex = 1;
1509    } else if (!isVirtual && (key == 'PageDown' || key == 'PageUp' || key == 'Home' || key == 'End')) {
1510        var firstDataIndex = thead && thead.rows && thead.rows.length && cellIndex != null ? thead.rows.length : 1;
1511        if (firstDataIndex > rows.length - 1) {
1512            firstDataIndex = rows.length - 1;
1513        }
1514        if (key == 'Home') {
1515            table.nextSelectedIndex = firstDataIndex;
1516        } else if (key == 'End') {
1517            table.nextSelectedIndex = rows.length - 1;
1518        } else {
1519            var pageFactor = 10;
1520            table.nextSelectedIndex = table.nextSelectedIndex + (key == 'PageDown' ? pageFactor : -pageFactor);
1521            if (table.nextSelectedIndex > rows.length - 1) {
1522                table.nextSelectedIndex = rows.length - 1;
1523            }
1524            if (table.nextSelectedIndex < firstDataIndex) {
1525                table.nextSelectedIndex = firstDataIndex;
1526            }
1527        }
1528    }
1529
1530    if (isVirtual && (key == 'ArrowDown' || key == 'ArrowUp')) {
1531        var minIndex = cellIndex != null ? 0 : 1;
1532        if (key == 'ArrowDown' && table.nextSelectedIndex >= rows.length - 1) {
1533            var rh = rows[rows.length - 1] ? rows[rows.length - 1].offsetHeight : 40;
1534            table.parentNode.scrollTop += rh;
1535            table.nextSelectedIndex = rows.length - 1;
1536        } else if (key == 'ArrowUp' && table.nextSelectedIndex <= minIndex) {
1537            var rh = rows[rows.length - 1] ? rows[rows.length - 1].offsetHeight : 40;
1538            table.parentNode.scrollTop -= rh;
1539            table.nextSelectedIndex = minIndex;
1540        }
1541    }
1542
1543    var activeId = gridId + '-active-item';
1544    var setActiveDescendant = function (el) {
1545        var prev = document.getElementById(activeId);
1546        if (prev && prev !== el) {
1547            prev.removeAttribute('id');
1548        }
1549        if (el && el.id !== activeId) { el.id = activeId; }
1550        if (el) {
1551            var roleTarget = grid.matches('[role="grid"],[role="treegrid"]')
1552                ? grid
1553                : grid.querySelector('[role="grid"],[role="treegrid"]') || grid;
1554            roleTarget.setAttribute('aria-activedescendant', activeId);
1555        }
1556    };
1557
1558    if (key == 'ArrowLeft' || key == 'ArrowRight' || (key == 'ArrowUp' && cellIndex != null && table.nextSelectedIndex == 0 && table.parentNode.scrollTop == 0)) {
1559        var highlightedCells = rows[table.nextSelectedIndex].querySelectorAll('.rz-state-focused');
1560        if (highlightedCells.length) {
1561            for (var i = 0; i < highlightedCells.length; i++) {
1562                highlightedCells[i].classList.remove('rz-state-focused');
1563            }
1564        }
1565
1566        if (
1567            table.nextSelectedCellIndex >= 0 &&
1568            table.nextSelectedCellIndex <= rows[table.nextSelectedIndex].cells.length - 1
1569        ) {
1570            var cell = rows[table.nextSelectedIndex].cells[table.nextSelectedCellIndex];
1571
1572            if (!cell.classList.contains('rz-state-focused')) {
1573                cell.classList.add('rz-state-focused');
1574                if (!isVirtual && table.parentElement.scrollWidth > table.parentElement.clientWidth) {
1575                    Radzen.scrollIntoViewIfNeeded(cell);
1576                }
1577            }
1578            setActiveDescendant(cell);
1579        }
1580    } else if (key == 'ArrowDown' || key == 'ArrowUp' || (!isVirtual && (key == 'PageDown' || key == 'PageUp' || key == 'Home' || key == 'End'))) {
1581        var highlighted = table.querySelectorAll('.rz-state-focused');
1582        if (highlighted.length) {
1583            for (var i = 0; i < highlighted.length; i++) {
1584                highlighted[i].classList.remove('rz-state-focused');
1585            }
1586        }
1587
1588        if (table.nextSelectedIndex >= 0 &&
1589            table.nextSelectedIndex <= rows.length - 1
1590        ) {
1591            var row = rows[table.nextSelectedIndex];
1592
1593            if (!row.classList.contains('rz-state-focused')) {
1594                row.classList.add('rz-state-focused');
1595                if (!isVirtual && table.parentElement.scrollHeight > table.parentElement.clientHeight) {
1596                    Radzen.scrollIntoViewIfNeeded(row);
1597                }
1598            }
1599            setActiveDescendant(row);
1600        }
1601    }
1602
1603    return [table.nextSelectedIndex, table.nextSelectedCellIndex];
1604  },
1605  uploadInputChange: function (e, url, auto, multiple, clear, parameterName, method = 'POST', stream = false) {
1606      if (auto) {
1607          Radzen.upload(e.target, url, multiple, clear, parameterName, method, stream);
1608          e.target.value = '';
1609      } else {
1610          Radzen.uploadChange(e.target);
1611      }
1612  },
1613  uploads: function (uploadComponent, id) {
1614    if (!Radzen.uploadComponents) {
1615      Radzen.uploadComponents = {};
1616    }
1617    Radzen.uploadComponents[id] = uploadComponent;
1618  },
1619  uploadChange: function (fileInput) {
1620    var files = [];
1621    for (var i = 0; i < fileInput.files.length; i++) {
1622      var file = fileInput.files[i];
1623      files.push({
1624        Name: file.name,
1625        Size: file.size,
1626        Url: URL.createObjectURL(file)
1627      });
1628    }
1629
1630    var uploadComponent =
1631      Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
1632    if (uploadComponent) {
1633      if (uploadComponent.localFiles) {
1634        // Clear any previously created preview URL(s)
1635        for (var i = 0; i < uploadComponent.localFiles.length; i++) {
1636          var file = uploadComponent.localFiles[i];
1637          if (file.Url) {
1638            URL.revokeObjectURL(file.Url);
1639          }
1640        }
1641      }
1642
1643      uploadComponent.files = Array.from(fileInput.files);
1644      uploadComponent.localFiles = files;
1645      try { suppressDisposed(uploadComponent.invokeMethodAsync('RadzenUpload.OnChange', files)); } catch { }
1646    }
1647
1648    for (var i = 0; i < fileInput.files.length; i++) {
1649      var file = fileInput.files[i];
1650      if (file.Url) {
1651        URL.revokeObjectURL(file.Url);
1652      }
1653    }
1654  },
1655  removeFileFromUpload: function (ref, name, id) {
1656    var uploadComponent = (Radzen.uploadComponents && Radzen.uploadComponents[ref]) ?? Radzen.uploadComponents[id];
1657    if (!uploadComponent) return;
1658    if (!uploadComponent.files) return;
1659    var file = uploadComponent.files.find(function (f) { return f.name == name; })
1660    if (!file) { return; }
1661    var localFile = uploadComponent.localFiles.find(function (f) { return f.Name == name; });
1662    if (localFile) {
1663      URL.revokeObjectURL(localFile.Url);
1664      var localIndex = uploadComponent.localFiles.indexOf(localFile);
1665      if (localIndex != -1) {
1666        uploadComponent.localFiles.splice(localIndex, 1);
1667      }
1668    }
1669    var index = uploadComponent.files.indexOf(file)
1670    if (index != -1) {
1671        uploadComponent.files.splice(index, 1);
1672    }
1673    var fileInput = document.getElementById(id).querySelector('input[type="file"]');
1674    if (fileInput && uploadComponent.files.length == 0) {
1675        fileInput.value = '';
1676    }
1677  },
1678  removeFileFromFileInput: function (fileInput) {
1679    fileInput.value = '';
1680  },
1681  upload: function (fileInput, url, multiple, clear, parameterName, method = 'POST', stream = false) {
1682    var uploadComponent = Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
1683    if (!uploadComponent) { return; }
1684    if (!uploadComponent.files || clear) {
1685        uploadComponent.files = Array.from(fileInput.files);
1686    }
1687    
1688	function asFormData() {
1689		var data = new FormData();
1690		var files = [];
1691		for (var i = 0; i < uploadComponent.files.length; i++) {
1692			var file = uploadComponent.files[i];
1693			data.append(parameterName || (multiple ? 'files' : 'file'), file, file.name);
1694			files.push({Name: file.name, Size: file.size});
1695		}
1696		return {data, files}
1697	}
1698	
1699	function asStream() {
1700		if (uploadComponent.files.length > 0) {
1701			var file = uploadComponent.files[0];
1702			return {data: file, files: [{Name: file.name, Size: file.size}]};
1703		}
1704		return {data: null, files: []}
1705	}
1706
1707  	var cancelled = false;
1708	var {data, files} = stream ? asStream() : asFormData();
1709	
1710    var xhr = new XMLHttpRequest();
1711    xhr.withCredentials = true;
1712    xhr.upload.onprogress = function (e) {
1713      if (e.lengthComputable) {
1714        var uploadComponent =
1715          Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
1716        if (uploadComponent) {
1717          var progress = parseInt((e.loaded / e.total) * 100);
1718          try { uploadComponent.invokeMethodAsync(
1719            'RadzenUpload.OnProgress',
1720            progress,
1721            e.loaded,
1722            e.total,
1723            files,
1724            cancelled
1725          ).then(function (cancel) {
1726              if (cancel) {
1727                  cancelled = true;
1728                  xhr.abort();
1729              }
1730          }); } catch { }
1731        }
1732      }
1733    };
1734    xhr.onreadystatechange = function (e) {
1735      if (xhr.readyState === XMLHttpRequest.DONE) {
1736        var status = xhr.status;
1737        var uploadComponent =
1738          Radzen.uploadComponents && Radzen.uploadComponents[fileInput.id];
1739        if (uploadComponent) {
1740          if (status === 0 || (status >= 200 && status < 400)) {
1741            try { uploadComponent.invokeMethodAsync(
1742              'RadzenUpload.OnComplete',
1743                xhr.responseText,
1744                cancelled
1745            ); } catch { }
1746          } else {
1747            try { uploadComponent.invokeMethodAsync(
1748              'RadzenUpload.OnError',
1749              xhr.responseText
1750            ); } catch { }
1751          }
1752        }
1753      }
1754    };
1755    try { uploadComponent.invokeMethodAsync('GetHeaders').then(function (headers) {
1756      xhr.open(method, url, true);
1757      for (var name in headers) {
1758        xhr.setRequestHeader(name, headers[name]);
1759      }
1760      xhr.send(data);
1761    }); } catch { }
1762  },
1763  getCookie: function (name) {
1764    var value = '; ' + decodeURIComponent(document.cookie);
1765    var parts = value.split('; ' + name + '=');
1766    if (parts.length == 2) return parts.pop().split(';').shift();
1767  },
1768  getCulture: function () {
1769    var cultureCookie = Radzen.getCookie('.AspNetCore.Culture');
1770    var uiCulture = cultureCookie
1771      ? cultureCookie.split('|').pop().split('=').pop()
1772      : null;
1773    return uiCulture || 'en-US';
1774  },
1775  numericOnPaste: function (e, min, max, locale = navigator.language) {
1776    if (!e.clipboardData) return;
1777
1778    let value = e.clipboardData.getData("text");
1779    if (!value) {
1780        e.preventDefault();
1781        return;
1782    }
1783
1784    value = String(value).trim();
1785
1786    const parts = new Intl.NumberFormat(locale).formatToParts(1234567.89);
1787
1788    let group = ",";
1789    let decimal = ".";
1790
1791    for (const p of parts) {
1792        if (p.type === "group") group = p.value;
1793        if (p.type === "decimal") decimal = p.value;
1794    }
1795
1796    value = value.replace(/[\u00A0\u202F]/g, " ");
1797
1798    if (group) {
1799        const escapedGroup = group.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1800        value = value.replace(new RegExp(escapedGroup, "g"), "");
1801    }
1802
1803    if (group === " ") {
1804        value = value.replace(/ /g, "");
1805    }
1806
1807    if (decimal !== ".") {
1808        const escapedDecimal = decimal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1809        value = value.replace(new RegExp(escapedDecimal, "g"), ".");
1810    }
1811
1812    if (!/^[+-]?(\d+(\.\d*)?|\.\d+)$/.test(value)) {
1813        e.preventDefault();
1814        return;
1815    }
1816
1817    const numericValue = Number(value);
1818
1819    if (!Number.isFinite(numericValue)) {
1820        e.preventDefault();
1821        return;
1822    }
1823
1824    if (min != null && numericValue < min) {
1825        e.preventDefault();
1826        return;
1827    }
1828
1829    if (max != null && numericValue > max) {
1830        e.preventDefault();
1831        return;
1832    }
1833  },
1834  numericOnInput: function (e, min, max, isNullable) {
1835      var value = e.target.value;
1836
1837      if (!isNullable && value == '' && min != null) {
1838          e.target.value = min;
1839      }
1840
1841      if (value && !isNaN(+value)) {
1842        var numericValue = +value;
1843        if (min != null && !isNaN(+min) && numericValue < min) {
1844            e.target.value = min;
1845        }
1846        if (max != null && !isNaN(+max) && numericValue > max) {
1847            e.target.value = max;
1848        }
1849      }
1850  },
1851  numericKeyPress: function (e, isInteger, decimalSeparator) {
1852    if (
1853      e.metaKey ||
1854      e.ctrlKey ||
1855      e.keyCode == 9 ||
1856      e.keyCode == 8 ||
1857      e.keyCode == 13
1858    ) {
1859      return;
1860      }
1861
1862      if (e.code === 'NumpadDecimal' && !isInteger) {
1863          var cursorPosition = e.target.selectionEnd;
1864          e.target.value = [e.target.value.slice(0, e.target.selectionStart), decimalSeparator, e.target.value.slice(e.target.selectionEnd)].join('');
1865          e.target.selectionStart = ++cursorPosition;
1866          e.target.selectionEnd = cursorPosition;
1867          e.preventDefault();
1868          return;
1869    }
1870
1871    var ch = e.key;
1872
1873    if (/\p{Nd}/u.test(ch) || ch === '-' || (!isInteger && ch === decimalSeparator)) {
1874      return;
1875    }
1876
1877    e.preventDefault();
1878  },
1879  openContextMenu: function (x,y,id, instance, callback) {
1880    Radzen.closePopup(id);
1881
1882    var invoker = document.activeElement;
1883    if (invoker && invoker !== document.body) {
1884        Radzen[id + 'contextMenuInvoker'] = invoker;
1885    }
1886
1887    Radzen.openPopup(null, id, false, null, x, y, instance, callback);
1888
1889    requestAnimationFrame(function () {
1890        var popup = document.getElementById(id);
1891        if (popup) {
1892            var menu = popup.querySelector('.rz-menu');
1893            if (menu) {
1894                menu.focus();
1895            }
1896        }
1897    });
1898  },
1899  restoreContextMenuFocus: function (id) {
1900    var invoker = Radzen[id + 'contextMenuInvoker'];
1901    Radzen[id + 'contextMenuInvoker'] = null;
1902    if (invoker && document.body.contains(invoker) && typeof invoker.focus === 'function') {
1903        invoker.focus();
1904    }
1905  },
1906  openTooltip: function (target, id, delay, duration, position, closeTooltipOnDocumentClick, instance, callback, clientX, clientY) {
1907    Radzen.closeTooltip(id);
1908
1909    if (clientX != null && clientY != null) {
1910        position = null;
1911        disableSmartPosition = true;
1912    }
1913    else {
1914        disableSmartPosition = false;
1915    }
1916
1917    if (target) {
1918        target.setAttribute('aria-describedby', id);
1919        Radzen[id + 'target'] = target;
1920    }
1921
1922    if (delay) {
1923        Radzen[id + 'delay'] = setTimeout(Radzen.openPopup, delay, target, id, false, position, clientX, clientY, instance, callback, closeTooltipOnDocumentClick, false, disableSmartPosition);
1924    } else {
1925        Radzen.openPopup(target, id, false, position, clientX, clientY, instance, callback, closeTooltipOnDocumentClick, false, disableSmartPosition);
1926    }
1927
1928    if (duration) {
1929      Radzen[id + 'duration'] = setTimeout(Radzen.closePopup, duration, id, instance, callback);
1930    }
1931
1932    // WCAG 1.4.13 (Content on Hover or Focus): the tooltip stays visible as long as
1933    // the pointer is over it, so the user can move onto and read/interact with it.
1934    var tooltipPopup = document.getElementById(id);
1935    if (tooltipPopup) {
1936        Radzen[id + 'tooltipEnter'] = function () {
1937            if (Radzen[id + 'duration']) {
1938                clearTimeout(Radzen[id + 'duration']);
1939                Radzen[id + 'duration'] = null;
1940            }
1941        };
1942        Radzen[id + 'tooltipLeave'] = function () {
1943            if (duration) {
1944                Radzen[id + 'duration'] = setTimeout(Radzen.closePopup, duration, id, instance, callback);
1945            }
1946        };
1947        tooltipPopup.addEventListener('mouseenter', Radzen[id + 'tooltipEnter']);
1948        tooltipPopup.addEventListener('mouseleave', Radzen[id + 'tooltipLeave']);
1949    }
1950
1951    // WAI-ARIA APG Tooltip pattern: Escape dismisses the tooltip.
1952    Radzen[id + 'esc'] = function (e) {
1953        if (e.key === 'Escape' || e.keyCode === 27) {
1954            Radzen.closeTooltip(id);
1955            if (instance && callback) {
1956                try { suppressDisposed(instance.invokeMethodAsync(callback, null)); } catch (ex) { }
1957            }
1958        }
1959    };
1960    document.addEventListener('keydown', Radzen[id + 'esc']);
1961  },
1962  closeTooltip(id) {
1963    Radzen.activeElement = null;
1964
1965    var target = Radzen[id + 'target'];
1966    if (target) {
1967        target.removeAttribute('aria-describedby');
1968        Radzen[id + 'target'] = null;
1969    }
1970
1971    if (Radzen[id + 'esc']) {
1972        document.removeEventListener('keydown', Radzen[id + 'esc']);
1973        Radzen[id + 'esc'] = null;
1974    }
1975
1976    var tooltipPopup = document.getElementById(id);
1977    if (tooltipPopup) {
1978        if (Radzen[id + 'tooltipEnter']) {
1979            tooltipPopup.removeEventListener('mouseenter', Radzen[id + 'tooltipEnter']);
1980            Radzen[id + 'tooltipEnter'] = null;
1981        }
1982        if (Radzen[id + 'tooltipLeave']) {
1983            tooltipPopup.removeEventListener('mouseleave', Radzen[id + 'tooltipLeave']);
1984            Radzen[id + 'tooltipLeave'] = null;
1985        }
1986    }
1987
1988    Radzen.closePopup(id);
1989
1990    if (Radzen[id + 'delay']) {
1991        clearTimeout(Radzen[id + 'delay']);
1992    }
1993
1994    if (Radzen[id + 'duration']) {
1995        clearTimeout(Radzen[id + 'duration']);
1996    }
1997  },
1998  findPopup: function (id) {
1999    var popups = [];
2000    for (var i = 0; i < document.body.children.length; i++) {
2001      if (document.body.children[i].id == id) {
2002        popups.push(document.body.children[i]);
2003      }
2004    }
2005    return popups;
2006  },
2007  repositionPopup: function (parent, id) {
2008      var popup = document.getElementById(id);
2009      if (!popup) return;
2010
2011      var rect = popup.getBoundingClientRect();
2012      var parentRect = parent ? parent.getBoundingClientRect() : { top: 0, bottom: 0, left: 0, right: 0, width: 0, height: 0 };
2013
2014      if (/Edge/.test(navigator.userAgent)) {
2015          var scrollTop = document.body.scrollTop;
2016      } else {
2017          var scrollTop = document.documentElement.scrollTop;
2018      }
2019
2020      var top = parentRect.bottom + scrollTop;
2021
2022      if (top + rect.height > window.innerHeight + scrollTop && parentRect.top > rect.height) {
2023          top = parentRect.top - rect.height + scrollTop;
2024      }
2025
2026      popup.style.top = top + 'px';
2027  },
2028  setPopupAriaExpanded: function (parent, id, expanded) {
2029    if (!parent || !id) return;
2030    var control = null;
2031    if (parent.getAttribute && parent.getAttribute('aria-controls') === id) {
2032      control = parent;
2033    } else if (parent.querySelector) {
2034      control = parent.querySelector('[aria-controls="' + (window.CSS && CSS.escape ? CSS.escape(id) : id) + '"]');
2035    }
2036    if (!control && parent.getAttribute && parent.getAttribute('role') === 'combobox') {
2037      control = parent;
2038    }
2039    if (control) {
2040      control.setAttribute('aria-expanded', expanded ? 'true' : 'false');
2041    }
2042  },
2043  openPopup: function (parent, id, syncWidth, position, x, y, instance, callback, closeOnDocumentClick = true, autoFocusFirstElement = false, disableSmartPosition = false) {
2044    var popup = document.getElementById(id);
2045      if (!popup || popup && popup.style.display == 'block' && popup.classList.contains('rz-autocomplete-panel')) return;
2046
2047    Radzen.activeElement = document.activeElement;
2048
2049    var parentRect = parent ? parent.getBoundingClientRect() : { top: y || 0, bottom: 0, left: x || 0, right: 0, width: 0, height: 0 };
2050
2051    if (/Edge/.test(navigator.userAgent)) {
2052      var scrollLeft = document.body.scrollLeft;
2053      var scrollTop = document.body.scrollTop;
2054    } else {
2055      var scrollLeft = document.documentElement.scrollLeft;
2056      var scrollTop = document.documentElement.scrollTop;
2057    }
2058
2059    var top = y ? y : parentRect.bottom;
2060    var left = x ? x : parentRect.left;
2061
2062      if (syncWidth) {
2063        popup.style.width = parentRect.width + 'px';
2064        if (!popup.style.minWidth) {
2065            popup.minWidth = true;
2066            popup.style.minWidth = parentRect.width + 'px';
2067        }
2068    }
2069
2070    if (window.chrome) {
2071        var closestFrozenCell = popup.closest('.rz-frozen-cell');
2072        if (closestFrozenCell) {
2073            Radzen[id + 'FZL'] = { cell: closestFrozenCell, left: closestFrozenCell.style.left };
2074            closestFrozenCell.style.left = '';
2075        }
2076    }
2077
2078    popup.style.display = 'block';
2079    popup.style.visibility = 'hidden';
2080    popup.onanimationend = null;
2081    popup.onanimationcancel = null;
2082    popup.classList.remove("rz-close");
2083    Radzen.setPopupAriaExpanded(parent, id, true);
2084
2085    var rect = popup.getBoundingClientRect();
2086    rect.width = x ? rect.width + 20 : rect.width;
2087    rect.height = y ? rect.height + 20 : rect.height;
2088
2089    var isRTL = Radzen.isRTL(popup);
2090
2091    if (isRTL && parent && (!position || position == 'bottom' || position == 'top')) {
2092      left = parentRect.right - rect.width;
2093    }
2094
2095    var smartPosition = !position || position == 'bottom';
2096
2097    if (smartPosition && top + rect.height > window.innerHeight && parentRect.top > rect.height) {
2098        if (disableSmartPosition !== true) {
2099            top = parentRect.top - rect.height;
2100        }
2101    }
2102
2103    if (smartPosition && left + rect.width > window.innerWidth && window.innerWidth > rect.width) {
2104      left = window.innerWidth - rect.width;
2105    }
2106
2107    if (smartPosition && isRTL && left < 0 && window.innerWidth > rect.width) {
2108      left = 0;
2109    }
2110
2111    if (position && !(disableSmartPosition === true && x != null && y != null)) {
2112      var tooltipPlacement = function (side) {
2113        switch (side) {
2114          case 'left':  return { left: parentRect.left - rect.width - 5, top: parentRect.top };
2115          case 'right': return { left: parentRect.right + 10, top: parentRect.top };
2116          case 'top':   return { left: isRTL ? parentRect.right - rect.width : parentRect.left, top: parentRect.top - rect.height + 5 };
2117          default:      return { left: isRTL ? parentRect.right - rect.width : parentRect.left, top: parentRect.bottom + 20 };
2118        }
2119      };
2120
2121      var tooltipFits = function (p) {
2122        return p.left >= 0 && p.top >= 0 &&
2123               p.left + rect.width <= window.innerWidth &&
2124               p.top + rect.height <= window.innerHeight;
2125      };
2126
2127      var fallbackOrder = {
2128        left: ['left', 'right', 'top', 'bottom'],
2129        right: ['right', 'left', 'top', 'bottom'],
2130        top: ['top', 'bottom', 'right', 'left'],
2131        bottom: ['bottom', 'top', 'right', 'left']
2132      }[position] || [position];
2133
2134      var tooltipContent = popup.children[0];
2135      var tooltipSide = position;
2136      var setTooltipSide = function (side) {
2137        if (side == tooltipSide || !tooltipContent || !tooltipContent.classList.contains('rz-' + tooltipSide + '-tooltip-content')) return;
2138        tooltipContent.classList.remove('rz-' + tooltipSide + '-tooltip-content');
2139        tooltipContent.classList.add('rz-' + side + '-tooltip-content');
2140        tooltipSide = side;
2141        rect = popup.getBoundingClientRect();
2142      };
2143
2144      var resolvedPosition = null;
2145      var placement = null;
2146
2147      for (var pi = 0; pi < fallbackOrder.length; pi++) {
2148        setTooltipSide(fallbackOrder[pi]);
2149        var candidate = tooltipPlacement(fallbackOrder[pi]);
2150        if (tooltipFits(candidate)) {
2151          resolvedPosition = fallbackOrder[pi];
2152          placement = candidate;
2153          break;
2154        }
2155      }
2156
2157      if (!placement) {
2158        setTooltipSide(position);
2159        resolvedPosition = position;
2160        placement = tooltipPlacement(position);
2161        placement.left = Math.max(0, Math.min(placement.left, window.innerWidth - rect.width));
2162        placement.top = Math.max(0, Math.min(placement.top, window.innerHeight - rect.height));
2163      }
2164
2165      left = placement.left;
2166      top = placement.top;
2167
2168      if (resolvedPosition != position) {
2169        position = resolvedPosition;
2170        if (instance && callback) {
2171          try { suppressDisposed(instance.invokeMethodAsync(callback, position)); } catch { }
2172        }
2173      }
2174    }
2175
2176    if (disableSmartPosition && x != null && y != null) {
2177        left = x + 10;
2178        top = y + 10;
2179        var tooltipContent = popup.children[0];
2180        var tooltipContentClassName = 'rz-bottom-tooltip-content';
2181        if (tooltipContent.classList.contains(tooltipContentClassName)) {
2182            tooltipContent.classList.remove(tooltipContentClassName);
2183        }
2184
2185        if (left + rect.width > window.innerWidth && window.innerWidth > rect.width) {
2186            top = parentRect.top - rect.height;
2187        }
2188
2189        if (left + rect.width > window.innerWidth && window.innerWidth > rect.width) {
2190            left = window.innerWidth - rect.width;
2191        }
2192    }
2193
2194    popup.style.zIndex = 2000;
2195    popup.style.left = left + scrollLeft + 'px';
2196    popup.style.top = top + scrollTop + 'px';
2197
2198    if (!popup.classList.contains('rz-overlaypanel')) {
2199        popup.classList.add('rz-popup');
2200    }
2201
2202    Radzen[id] = function (e) {
2203        var lastPopup = Radzen.popups && Radzen.popups[Radzen.popups.length - 1];
2204        var currentPopup = lastPopup != null && document.getElementById(lastPopup.id) || popup;
2205
2206        if (lastPopup) {
2207            currentPopup.instance = lastPopup.instance;
2208            currentPopup.callback = lastPopup.callback;
2209            currentPopup.parent = lastPopup.parent;
2210        }
2211
2212        if(e.type == 'contextmenu' || !e.target || !closeOnDocumentClick) return;
2213        if (!/Android/i.test(navigator.userAgent) &&
2214            !['input', 'textarea'].includes(document.activeElement ? document.activeElement.tagName.toLowerCase() : '') && e.type == 'resize') {
2215            Radzen.closePopup(currentPopup.id, currentPopup.instance, currentPopup.callback, e);
2216            return;
2217        }
2218
2219        var closestLink = e.target.closest && (e.target.closest('.rz-link') || e.target.closest('.rz-navigation-item-link'));
2220        if (e.type == 'resize' && !/Android/i.test(navigator.userAgent) &&
2221            !['input', 'textarea'].includes(document.activeElement ? document.activeElement.tagName.toLowerCase() : '')) {
2222            if (closestLink && closestLink.closest && closestLink.closest('a') && e.button == 0) {
2223                closestLink.closest('a').click();
2224                Radzen.closeAllPopups();
2225            } else {
2226                Radzen.closeAllPopups();
2227            }
2228        }
2229        if (currentPopup.parent) {
2230          if (e.type == 'mousedown' && !currentPopup.parent.contains(e.target) && !currentPopup.contains(e.target)) {
2231              Radzen.closePopup(currentPopup.id, currentPopup.instance, currentPopup.callback, e);
2232          }
2233        } else {
2234          if (e.target.nodeType && !currentPopup.contains(e.target)) {
2235              Radzen.closePopup(currentPopup.id, currentPopup.instance, currentPopup.callback, e);
2236          }
2237        }
2238    };
2239
2240    if (!Radzen.popups) {
2241        Radzen.popups = [];
2242    }
2243
2244    Radzen.popups.push({ id, instance, callback, parent });
2245
2246    if (popup.parentNode && popup.parentNode !== document.body) {
2247        popup.__radzenHome = popup.parentNode;
2248    }
2249
2250    document.body.appendChild(popup);
2251
2252    if (!position) {
2253        var popupRect = popup.getBoundingClientRect();
2254        // Clamp into the viewport. If the popup is smaller than the viewport, shift it
2255        // so its right/bottom edge fits. If it is larger than the viewport, pin the
2256        // top/left corner to the viewport origin so content is at least reachable
2257        // (instead of leaving it positioned off-screen at the anchor).
2258        if (popupRect.right > window.innerWidth) {
2259            popup.style.left = Math.max(scrollLeft, window.innerWidth - popupRect.width + scrollLeft) + 'px';
2260        }
2261        if (popupRect.bottom > window.innerHeight) {
2262            popup.style.top = Math.max(scrollTop, window.innerHeight - popupRect.height + scrollTop) + 'px';
2263        }
2264    }
2265
2266    popup.style.visibility = '';
2267    popup.classList.add("rz-open");
2268
2269    document.removeEventListener('mousedown', Radzen[id]);
2270    document.addEventListener('mousedown', Radzen[id]);
2271    window.removeEventListener('resize', Radzen[id]);
2272    window.addEventListener('resize', Radzen[id]);
2273
2274    if (window.visualViewport && parent &&
2275        (popup.classList.contains('rz-dropdown-panel') || popup.classList.contains('rz-multiselect-panel'))) {
2276        var itemsWrapper = popup.querySelector('.rz-dropdown-items-wrapper, .rz-multiselect-items-wrapper');
2277        if (itemsWrapper) {
2278            popup.__originalWrapperMaxHeight = itemsWrapper.style.maxHeight;
2279            popup.__originalPopupTop = popup.style.top;
2280            popup.__viewportResizeHandler = function () {
2281                if (popup.style.display !== 'block') return;
2282                var vv = window.visualViewport;
2283                var viewportTop = vv.offsetTop;
2284                var viewportBottom = viewportTop + vv.height;
2285                var pRect = parent.getBoundingClientRect();
2286                var popRect = popup.getBoundingClientRect();
2287                if (popRect.top >= viewportTop - 1 && popRect.bottom <= viewportBottom + 1) {
2288                    itemsWrapper.style.maxHeight = popup.__originalWrapperMaxHeight;
2289                    popup.style.top = popup.__originalPopupTop;
2290                    return;
2291                }
2292                var spaceBelow = viewportBottom - pRect.bottom - 5;
2293                var spaceAbove = pRect.top - viewportTop - 5;
2294                var sTop = /Edge/.test(navigator.userAgent) ? document.body.scrollTop : document.documentElement.scrollTop;
2295                if (spaceBelow >= spaceAbove) {
2296                    popup.style.top = (pRect.bottom + sTop) + 'px';
2297                    itemsWrapper.style.maxHeight = Math.max(60, spaceBelow) + 'px';
2298                } else {
2299                    itemsWrapper.style.maxHeight = Math.max(60, spaceAbove) + 'px';
2300                    var newHeight = popup.getBoundingClientRect().height;
2301                    popup.style.top = (pRect.top - newHeight + sTop) + 'px';
2302                }
2303            };
2304            window.visualViewport.addEventListener('resize', popup.__viewportResizeHandler);
2305        }
2306    }
2307
2308    if (popup.__contentResizeObserver) {
2309        popup.__contentResizeObserver.disconnect();
2310        delete popup.__contentResizeObserver;
2311    }
2312
2313    if (!position && parent && disableSmartPosition !== true && !popup.__viewportResizeHandler && typeof ResizeObserver !== 'undefined') {
2314        var observedHeight = popup.getBoundingClientRect().height;
2315        popup.__contentResizeObserver = new ResizeObserver(function () {
2316            if (popup.style.display !== 'block') return;
2317            var height = popup.getBoundingClientRect().height;
2318            if (height === observedHeight) return;
2319            observedHeight = height;
2320            Radzen.repositionPopup(parent, id);
2321        });
2322        popup.__contentResizeObserver.observe(popup);
2323    }
2324
2325    var p = parent;
2326    while (p && p != document.body) {
2327        if (p.scrollWidth > p.clientWidth || p.scrollHeight > p.clientHeight) {
2328            p.removeEventListener('scroll', Radzen.closeAllPopups);
2329            p.addEventListener('scroll', Radzen.closeAllPopups);
2330        }
2331        p = p.parentElement;
2332    }
2333
2334    if (!parent) {
2335        document.removeEventListener('contextmenu', Radzen[id]);
2336        document.addEventListener('contextmenu', Radzen[id]);
2337    }
2338
2339    if (autoFocusFirstElement) {
2340        setTimeout(function () {
2341            popup.removeEventListener('keydown', Radzen.focusTrap);
2342            popup.addEventListener('keydown', Radzen.focusTrap);
2343
2344            var focusable = Radzen.getFocusableElements(popup);
2345            var firstFocusable = focusable[0];
2346            if (firstFocusable) {
2347                firstFocusable.focus();
2348            }
2349        }, 200);
2350    }
2351
2352    if (popup.__escapeHandler) {
2353        popup.removeEventListener('keydown', popup.__escapeHandler, true);
2354    }
2355    popup.__escapeHandler = function (e) {
2356        if (e.key === 'Escape' || e.key === 'Esc') {
2357            var menu = popup.querySelector('.rz-menu[role="menu"]');
2358            if (menu && menu.querySelector('.rz-navigation-item-active')) {
2359                return;
2360            }
2361            Radzen.closePopup(id, instance, callback, e);
2362        }
2363        if (e.key === 'Tab' && parent && (popup.classList.contains('rz-dropdown-panel') || popup.classList.contains('rz-multiselect-panel'))) {
2364            e.preventDefault();
2365        }
2366        if (e.key === 'Tab' && !e.shiftKey) {
2367            var timepicker = popup.querySelector('.rz-timepicker');
2368            if (timepicker) {
2369                var focusable = Radzen.getFocusableElements(timepicker);
2370                var last = focusable[focusable.length - 1];
2371                if (last && document.activeElement === last) {
2372                    Radzen.closePopup(id, instance, callback, e);
2373                }
2374            }
2375        }
2376    };
2377    popup.addEventListener('keydown', popup.__escapeHandler, true);
2378  },
2379  closeAllPopups: function (e, id) {
2380    if (!Radzen.popups) return;
2381    var el = e && e.target || id && documentElement.getElementById(id);
2382    var popups = Radzen.popups;
2383      for (var i = 0; i < popups.length; i++) {
2384        var p = popups[i];
2385
2386        var closestPopup = el && el.closest && (el.closest('.rz-popup') || el.closest('.rz-overlaypanel'));
2387        if (closestPopup && closestPopup != p) {
2388            return;
2389        }
2390
2391        Radzen.closePopup(p.id, p.instance, p.callback, e);
2392    }
2393    Radzen.popups = [];
2394  },
2395  closePopup: function (id, instance, callback, e, preventFocusRestore) {
2396    var popup = document.getElementById(id);
2397    if (!popup) return;
2398    var popupInfo = (Radzen.popups || []).find(function (p) { return p.id === id; });
2399    if (popupInfo && popupInfo.parent) {
2400      Radzen.setPopupAriaExpanded(popupInfo.parent, id, false);
2401      var combobox = popupInfo.parent.closest('[role="combobox"]');
2402      if (combobox) {
2403        combobox.removeAttribute('aria-activedescendant');
2404      }
2405    }
2406    if (popup.style.display == 'none') {
2407        var popups = Radzen.findPopup(id);
2408        if (popups.length > 1) {
2409            for (var i = 0; i < popups.length; i++) {
2410                if (popups[i].style.display == 'none') {
2411                    popups[i].parentNode.removeChild(popups[i]);
2412                } else {
2413                    popup = popups[i];
2414                }
2415            }
2416        } else if (popups.length == 1 && popups[0].style.display != 'none') {
2417            popup = popups[0];
2418        } else {
2419            return;
2420        }
2421    }
2422
2423    if (popup) {
2424      if (popup.minWidth) {
2425          popup.style.minWidth = '';
2426      }
2427
2428      if (window.chrome && Radzen[id + 'FZL']) {
2429        Radzen[id + 'FZL'].cell.style.left = Radzen[id + 'FZL'].left;
2430        Radzen[id + 'FZL'] = null;
2431      }
2432
2433      var hidePopup = function () {
2434          popup.style.display = 'none';
2435          popup.onanimationend = null;
2436          popup.onanimationcancel = null;
2437      }
2438      popup.onanimationend = hidePopup;
2439      popup.onanimationcancel = hidePopup;
2440      popup.classList.add("rz-close");
2441      popup.classList.remove("rz-open");
2442      var closeAnimationRunning = popup.getAnimations
2443          ? popup.getAnimations().some(function (a) { return a.animationName == 'rz-close'; })
2444          : getComputedStyle(popup).animationName.indexOf('rz-close') != -1;
2445      if (!closeAnimationRunning) {
2446          hidePopup();
2447      }
2448    }
2449    if (popup && popup.__escapeHandler) {
2450        popup.removeEventListener('keydown', popup.__escapeHandler, true);
2451        delete popup.__escapeHandler;
2452    }
2453    if (popup && popup.__viewportResizeHandler) {
2454        if (window.visualViewport) {
2455            window.visualViewport.removeEventListener('resize', popup.__viewportResizeHandler);
2456        }
2457        var wrapperToReset = popup.querySelector('.rz-dropdown-items-wrapper, .rz-multiselect-items-wrapper');
2458        if (wrapperToReset && popup.__originalWrapperMaxHeight !== undefined) {
2459            wrapperToReset.style.maxHeight = popup.__originalWrapperMaxHeight;
2460        }
2461        delete popup.__viewportResizeHandler;
2462        delete popup.__originalWrapperMaxHeight;
2463        delete popup.__originalPopupTop;
2464    }
2465    if (popup && popup.__contentResizeObserver) {
2466        popup.__contentResizeObserver.disconnect();
2467        delete popup.__contentResizeObserver;
2468    }
2469    document.removeEventListener('mousedown', Radzen[id]);
2470    window.removeEventListener('resize', Radzen[id]);
2471    Radzen[id] = null;
2472
2473    if (instance && callback) {
2474        if (callback.includes('RadzenTooltip')) {
2475            try { suppressDisposed(instance.invokeMethodAsync(callback, null)); } catch { }
2476        } else {
2477            try { suppressDisposed(instance.invokeMethodAsync(callback)); } catch { }
2478        }
2479    }
2480    Radzen.popups = (Radzen.popups || []).filter(function (obj) {
2481        return obj.id !== id;
2482    });
2483
2484    var closedByOutsideMousedown = e && e.type == 'mousedown' && e.target && e.target.nodeType &&
2485        popup && !popup.contains(e.target) &&
2486        !(popupInfo && popupInfo.parent && popupInfo.parent.contains(e.target));
2487
2488    if (!preventFocusRestore && !closedByOutsideMousedown &&
2489        (Radzen.activeElement && Radzen.activeElement == document.activeElement ||
2490        Radzen.activeElement && document.activeElement == document.body ||
2491        Radzen.activeElement && popup && popup.contains(document.activeElement) ||
2492        Radzen.activeElement && document.activeElement &&
2493            (document.activeElement.classList.contains('rz-dropdown-filter') ||
2494             document.activeElement.classList.contains('rz-lookup-search-input') ||
2495             document.activeElement.classList.contains('rz-multiselect-filter-container') ||
2496             document.activeElement.closest('.rz-multiselect-filter-container') !== null))) {
2497        setTimeout(function () {
2498            if (e && e.target && e.target.tabIndex != -1) {
2499                Radzen.activeElement = e.target;
2500            }
2501            if (Radzen.activeElement && (document.activeElement == null || document.activeElement == document.body || popup && popup.contains(document.activeElement))) {
2502               Radzen.activeElement.focus();
2503            }
2504            Radzen.activeElement = null;
2505        }, 100);
2506    } else if (closedByOutsideMousedown) {
2507        Radzen.activeElement = null;
2508    }
2509  },
2510  popupOpened: function (id) {
2511    var popup = document.getElementById(id);
2512    if (popup) {
2513        return popup.style.display != 'none';
2514    }
2515    return false;
2516  },
2517  togglePopup: function (parent, id, syncWidth, instance, callback, closeOnDocumentClick = true, autoFocusFirstElement = false) {
2518    var popup = document.getElementById(id);
2519    if (!popup) return;
2520    if (popup.style.display == 'block') {
2521      Radzen.closePopup(id, instance, callback);
2522    } else {
2523      Radzen.openPopup(parent, id, syncWidth, null, null, null, instance, callback, closeOnDocumentClick, autoFocusFirstElement);
2524    }
2525  },
2526  destroyPopup: function (id) {
2527    var popup = document.getElementById(id);
2528    if (popup) {
2529      if (popup.__escapeHandler) {
2530          popup.removeEventListener('keydown', popup.__escapeHandler, true);
2531          delete popup.__escapeHandler;
2532      }
2533      if (popup.__viewportResizeHandler) {
2534          if (window.visualViewport) {
2535              window.visualViewport.removeEventListener('resize', popup.__viewportResizeHandler);
2536          }
2537          delete popup.__viewportResizeHandler;
2538          delete popup.__originalWrapperMaxHeight;
2539          delete popup.__originalPopupTop;
2540      }
2541      if (popup.__contentResizeObserver) {
2542          popup.__contentResizeObserver.disconnect();
2543          delete popup.__contentResizeObserver;
2544      }
2545      if (popup.__radzenHome && popup.__radzenHome.isConnected) {
2546          popup.__radzenHome.appendChild(popup);
2547      } else if (popup.parentNode === document.body) {
2548          document.body.removeChild(popup);
2549      }
2550      delete popup.__radzenHome;
2551    }
2552    document.removeEventListener('mousedown', Radzen[id]);
2553    document.removeEventListener('contextmenu', Radzen[id]);
2554    window.removeEventListener('resize', Radzen[id]);
2555    Radzen[id] = null;
2556    Radzen.popups = (Radzen.popups || []).filter(function (obj) {
2557        return obj.id !== id;
2558    });
2559  },
2560  scrollDataGrid: function (e) {
2561    var scrollLeft =
2562      (e.target.scrollLeft ? '-' + e.target.scrollLeft : 0) + 'px';
2563
2564      e.target.previousElementSibling.style.marginLeft = scrollLeft;
2565      e.target.previousElementSibling.firstElementChild.style.paddingRight =
2566          e.target.clientHeight < e.target.scrollHeight ? (e.target.offsetWidth - e.target.clientWidth) + 'px' : '0px';
2567
2568    if (e.target.nextElementSibling) {
2569        e.target.nextElementSibling.style.marginLeft = scrollLeft;
2570        e.target.nextElementSibling.firstElementChild.style.paddingRight =
2571            e.target.clientHeight < e.target.scrollHeight ? (e.target.offsetWidth - e.target.clientWidth) + 'px' : '0px';
2572    }
2573
2574    for (var i = 0; i < document.body.children.length; i++) {
2575        if (document.body.children[i].classList.contains('rz-overlaypanel')) {
2576            document.body.children[i].style.display = 'none';
2577        }
2578    }
2579  },
2580  focusFirstFocusableElement: function (el) {
2581      var focusable = Radzen.getFocusableElements(el);
2582      if (!focusable || !focusable.length) return;
2583
2584      var first = focusable[0];
2585      
2586      if (first.classList.contains('rz-html-editor-content')) {
2587          var sel = window.getSelection();
2588          var range = document.createRange();
2589          range.setStart(first, 0);
2590          range.setEnd(first, 0);
2591          sel.removeAllRanges();
2592          sel.addRange(range);
2593          first.focus();
2594      } else {
2595          first.focus();
2596      }
2597  },
2598  openSideDialog: function (options, dialogService) {
2599      if (dialogService) {
2600          Radzen.sideDialogService = dialogService;
2601      }
2602      if (!Radzen.sideDialogInvokerElement) {
2603          var invoker = document.activeElement;
2604          if (invoker && invoker !== document.body && !invoker.closest('.rz-dialog-side')) {
2605              Radzen.sideDialogInvokerElement = invoker;
2606          }
2607      }
2608      var sideDialog = document.querySelector('.rz-dialog-side');
2609      if (sideDialog) {
2610          sideDialog.removeEventListener('keydown', Radzen.focusTrap);
2611          if (options.showMask) {
2612              sideDialog.addEventListener('keydown', Radzen.focusTrap);
2613          }
2614          sideDialog.removeEventListener('keydown', Radzen.closeSideDialogOnEsc);
2615          sideDialog.addEventListener('keydown', Radzen.closeSideDialogOnEsc);
2616      }
2617      setTimeout(function () {
2618          if (options.autoFocusFirstElement) {
2619              var dialogs = document.querySelectorAll('.rz-dialog-side-content');
2620              if (dialogs.length == 0) return;
2621              var lastDialog = dialogs[dialogs.length - 1];
2622              Radzen.focusFirstFocusableElement(lastDialog);
2623          }
2624      }, 500);
2625  },
2626  closeSideDialog: function () {
2627      delete Radzen.sideDialogService;
2628      var invoker = Radzen.sideDialogInvokerElement;
2629      Radzen.sideDialogInvokerElement = null;
2630      if (invoker && typeof invoker.focus === 'function' && document.body.contains(invoker)) {
2631          invoker.focus();
2632      }
2633  },
2634  closeSideDialogOnEsc: function (e) {
2635      e = e || window.event;
2636      var isEscape = "key" in e ? (e.key === "Escape" || e.key === "Esc") : (e.keyCode === 27);
2637      if (isEscape && Radzen.sideDialogService) {
2638          var popups = document.querySelectorAll('.rz-popup,.rz-overlaypanel');
2639          for (var i = 0; i < popups.length; i++) {
2640              if (popups[i].style.display != 'none') {
2641                  return;
2642              }
2643          }
2644          try {
2645              Radzen.sideDialogService.invokeMethodAsync('DialogService.TryCloseSide', null);
2646          } catch (ex) { }
2647      }
2648  },
2649  createSideDialogResizer: function(handle, sideDialog, options) {
2650      const normalizeDir = (value) => {
2651          if (typeof value === 'string' && value.length) {
2652              return value.toLowerCase();
2653          }
2654          if (typeof value === 'number') {
2655              const positions = ['right', 'left', 'top', 'bottom'];
2656              return positions[value] || 'right';
2657          }
2658          return 'right';
2659      };
2660
2661      const dir = normalizeDir(options?.position);
2662
2663      let start = null;
2664
2665      const onDown = (e) => {
2666          e.preventDefault();
2667
2668          start = {x: e.clientX, y: e.clientY, w: sideDialog.clientWidth, h: sideDialog.clientHeight};
2669
2670          document.addEventListener('pointermove', onMove);
2671          document.addEventListener('pointerup', onUp, {once: true});
2672          document.addEventListener('pointercancel', onUp, {once: true});
2673      };
2674
2675      const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
2676
2677      const applyWidth = (w) => {
2678          sideDialog.style.width = Math.round(w) + 'px';
2679      };
2680      const applyHeight = (h) => {
2681          sideDialog.style.height = `${Math.round(h)}px`;
2682      };
2683
2684      const onMove = (e) => {
2685          if (!start) return;
2686
2687          const dx = e.clientX - start.x;
2688          const dy = e.clientY - start.y;
2689
2690          switch (dir) {
2691              case 'right':
2692                  applyWidth(clamp(start.w - dx, options.minWidth, Infinity));
2693                  break;
2694              case 'left':
2695                  applyWidth(clamp(start.w + dx, options.minWidth, Infinity));
2696                  break;
2697              case 'bottom':
2698                  applyHeight(clamp(start.h - dy, options.minHeight, Infinity));
2699                  break;
2700              case 'top':
2701                  applyHeight(clamp(start.h + dy, options.minHeight, Infinity));
2702                  break;
2703          }
2704      };
2705
2706      const onUp = (e) => {
2707          start = null;
2708          document.removeEventListener('pointermove', onMove);
2709      };
2710
2711      handle.addEventListener('pointerdown', onDown);
2712
2713      return {
2714          dispose() {
2715              handle.removeEventListener('pointerdown', onDown);
2716              document.removeEventListener('pointermove', onMove);
2717          }
2718      };
2719  },
2720  openDialog: function (options, dialogService, dialog) {
2721    if (!Radzen.dialogInvokerElement) {
2722        var invoker = document.activeElement;
2723        if (invoker && invoker !== document.body && !invoker.closest('.rz-dialog-content')) {
2724            Radzen.dialogInvokerElement = invoker;
2725        }
2726    }
2727    if (Radzen.closeAllPopups) {
2728        Radzen.closeAllPopups();
2729    }
2730    Radzen.dialogService = dialogService;
2731    if (
2732      document.documentElement.scrollHeight >
2733      document.documentElement.clientHeight
2734    ) {
2735      document.body.classList.add('no-scroll');
2736    }
2737
2738    var currentDialogs = document.querySelectorAll('.rz-dialog-content');
2739    var currentDialog = currentDialogs.length ? currentDialogs[currentDialogs.length - 1] : null;
2740    if (currentDialog) {
2741        currentDialog.options = options;
2742        var dialogRoot = currentDialog.closest('.rz-dialog') || currentDialog;
2743        dialogRoot.removeEventListener('keydown', Radzen.focusTrap);
2744        dialogRoot.addEventListener('keydown', Radzen.focusTrap);
2745    }
2746
2747    setTimeout(function () {
2748        var dialogs = document.querySelectorAll('.rz-dialog-content');
2749        if (dialogs.length == 0) return;
2750        var lastDialog = dialogs[dialogs.length - 1];
2751
2752        if (lastDialog) {
2753            lastDialog.options = options;
2754
2755            if (options.resizable) {
2756                var dialogElement = lastDialog.parentElement;
2757
2758                dialog.offsetWidth = dialogElement.offsetWidth;
2759                dialog.offsetHeight = dialogElement.offsetHeight;
2760
2761                var userResizing = false;
2762
2763                var pushSize = function () {
2764                    if (!dialog) return;
2765                    if (dialog.offsetWidth == dialogElement.offsetWidth &&
2766                        dialog.offsetHeight == dialogElement.offsetHeight) return;
2767
2768                    dialog.offsetWidth = dialogElement.offsetWidth;
2769                    dialog.offsetHeight = dialogElement.offsetHeight;
2770
2771                    dialog.invokeMethodAsync(
2772                        'RadzenDialog.OnResize',
2773                        dialogElement.offsetWidth,
2774                        dialogElement.offsetHeight
2775                    ).catch(function () { });
2776                };
2777
2778                var stopTracking = function () {
2779                    document.removeEventListener('pointermove', onPointerMove, true);
2780                    document.removeEventListener('pointerup', onPointerUp, true);
2781                    document.removeEventListener('pointercancel', onPointerUp, true);
2782                };
2783
2784                var onPointerMove = function (e) {
2785                    if (e.buttons === 1) {
2786                        userResizing = true;
2787                        return;
2788                    }
2789
2790                    onPointerUp();
2791                };
2792
2793                var onPointerUp = function () {
2794                    stopTracking();
2795                    if (!userResizing) return;
2796                    userResizing = false;
2797                    pushSize();
2798                };
2799
2800                var onPointerDown = function (e) {
2801                    if (e.button !== 0 || !e.isPrimary || e.target !== dialogElement) return;
2802
2803                    document.addEventListener('pointermove', onPointerMove, true);
2804                    document.addEventListener('pointerup', onPointerUp, true);
2805                    document.addEventListener('pointercancel', onPointerUp, true);
2806                };
2807
2808                var dialogResize = function (e) {
2809                    if (!dialog) return;
2810
2811                    if (!e[0].target.isConnected) {
2812                        resizer.dispose();
2813                        return;
2814                    }
2815
2816                    if (!userResizing) {
2817                        dialog.offsetWidth = e[0].target.offsetWidth;
2818                        dialog.offsetHeight = e[0].target.offsetHeight;
2819                        return;
2820                    }
2821
2822                    pushSize();
2823                };
2824
2825                var resizeObserver = new ResizeObserver(dialogResize);
2826                resizeObserver.observe(dialogElement);
2827
2828                dialogElement.addEventListener('pointerdown', onPointerDown);
2829
2830                var resizer = {
2831                    disposed: false,
2832                    dispose: function () {
2833                        if (this.disposed) return;
2834                        this.disposed = true;
2835                        resizeObserver.disconnect();
2836                        dialogElement.removeEventListener('pointerdown', onPointerDown);
2837                        stopTracking();
2838                        var index = Radzen.dialogResizers.indexOf(this);
2839                        if (index != -1) {
2840                            Radzen.dialogResizers.splice(index, 1);
2841                        }
2842                        if (Radzen.dialogResizer === this) {
2843                            Radzen.dialogResizer = null;
2844                        }
2845                    },
2846                    disconnect: function () { this.dispose(); }
2847                };
2848
2849                Radzen.dialogResizers = Radzen.dialogResizers || [];
2850                Radzen.dialogResizers.push(resizer);
2851                Radzen.dialogResizer = resizer;
2852            }
2853
2854            if (options.draggable) {
2855                var dialogTitle = lastDialog.parentElement.querySelector('.rz-dialog-titlebar');
2856                if (dialogTitle) {
2857                    dialogTitle.dragHandler = function (e) {
2858                        var rect = lastDialog.parentElement.getBoundingClientRect();
2859                        var offsetX = e.clientX - rect.left;
2860                        var offsetY = e.clientY - rect.top;
2861
2862                        var move = function (e) {
2863                            var left = e.clientX - offsetX;
2864                            var top = e.clientY - offsetY;
2865
2866                            lastDialog.parentElement.style.left = left + 'px';
2867                            lastDialog.parentElement.style.top = top + 'px';
2868
2869                            try { suppressDisposed(dialog.invokeMethodAsync('RadzenDialog.OnDrag', top, left)); } catch { }
2870                        };
2871
2872                        var stop = function () {
2873                            document.removeEventListener('mousemove', move);
2874                            document.removeEventListener('mouseup', stop);
2875                        };
2876
2877                        document.addEventListener('mousemove', move);
2878                        document.addEventListener('mouseup', stop);
2879                    };
2880
2881                    dialogTitle.addEventListener('mousedown', dialogTitle.dragHandler);
2882                }
2883            }
2884
2885            if (options.autoFocusFirstElement) {
2886                Radzen.focusFirstFocusableElement(lastDialog);
2887            }
2888        }
2889    }, 500);
2890
2891    document.removeEventListener('keydown', Radzen.closePopupOrDialog);
2892    if (options.closeDialogOnEsc) {
2893        document.addEventListener('keydown', Radzen.closePopupOrDialog);
2894    }
2895  },
2896  closeDialog: function () {
2897    var resizers = (Radzen.dialogResizers || []).slice();
2898    for (var i = 0; i < resizers.length; i++) {
2899        resizers[i].dispose();
2900    }
2901    Radzen.dialogResizers = [];
2902    Radzen.dialogResizer = null;
2903    document.body.classList.remove('no-scroll');
2904    var dialogs = document.querySelectorAll('.rz-dialog-content');
2905
2906    var lastDialog = dialogs.length && dialogs[dialogs.length - 1];
2907    if (lastDialog) {
2908        var dialogTitle = lastDialog.parentElement.querySelector('.rz-dialog-titlebar');
2909        if (dialogTitle && dialogTitle.dragHandler) {
2910            dialogTitle.removeEventListener('mousedown', dialogTitle.dragHandler);
2911            dialogTitle.dragHandler = null;
2912        }
2913    }
2914
2915    if (dialogs.length <= 1) {
2916        document.removeEventListener('keydown', Radzen.closePopupOrDialog);
2917        delete Radzen.dialogService;
2918        Radzen.restoreDialogInvokerFocus();
2919    }
2920  },
2921  restoreDialogInvokerFocus: function () {
2922    var invoker = Radzen.dialogInvokerElement;
2923    Radzen.dialogInvokerElement = null;
2924    if (invoker && typeof invoker.focus === 'function' && document.body.contains(invoker)) {
2925        invoker.focus();
2926    }
2927  },
2928  disableKeydown: function (e) {
2929      e = e || window.event;
2930      e.preventDefault();
2931  },
2932  getFocusableElements: function (element) {
2933    return [...element.querySelectorAll('a, button, input, textarea, select, details, iframe, embed, object, summary, dialog, audio[controls], video[controls], [contenteditable], [tabindex]')]
2934      .filter(el => {
2935        if (!el || el.hasAttribute('disabled') || el.offsetParent === null) return false;
2936    
2937        // If this is inside a .rz-html-editor with tabindex="-1", skip it
2938        var editorParent = el.closest('.rz-html-editor');
2939        if (editorParent && editorParent.hasAttribute('tabindex') && editorParent.tabIndex === -1) return false;
2940        else if (editorParent) return true;
2941    
2942        return el.tabIndex > -1;
2943    });
2944  },
2945  focusTrap: function (e) {
2946    e = e || window.event;
2947    var isTab = false;
2948    if ("key" in e) {
2949        isTab = (e.key === "Tab");
2950    } else {
2951        isTab = (e.keyCode === 9);
2952    }
2953    if (isTab) {
2954        var focusable = Radzen.getFocusableElements(e.currentTarget);
2955        var firstFocusable = focusable[0];
2956        var lastFocusable = focusable[focusable.length - 1];
2957
2958        if (firstFocusable && lastFocusable && e.shiftKey && document.activeElement === firstFocusable) {
2959            e.preventDefault();
2960            lastFocusable.focus();
2961        } else if (firstFocusable && lastFocusable && !e.shiftKey && document.activeElement === lastFocusable) {
2962            e.preventDefault();
2963            firstFocusable.focus();
2964        }
2965    }
2966  },
2967  closePopupOrDialog: function (e) {
2968      e = e || window.event;
2969      var isEscape = false;
2970      if ("key" in e) {
2971          isEscape = (e.key === "Escape" || e.key === "Esc");
2972      } else {
2973          isEscape = (e.keyCode === 27);
2974      }
2975      if (isEscape && Radzen.dialogService) {
2976          var popups = document.querySelectorAll('.rz-popup,.rz-overlaypanel');
2977          for (var i = 0; i < popups.length; i++) {
2978              if (popups[i].style.display != 'none') {
2979                  return;
2980              }
2981          }
2982
2983          var dialogs = document.querySelectorAll('.rz-dialog-content');
2984          if (dialogs.length == 0) return;
2985          var lastDialog = dialogs[dialogs.length - 1];
2986
2987          if (lastDialog && lastDialog.options && lastDialog.options.closeDialogOnEsc) {
2988              try {
2989                  Radzen.dialogService.invokeMethodAsync('DialogService.TryClose', null).then(function(closed) {
2990                      if (closed && dialogs.length <= 1) {
2991                          document.removeEventListener('keydown', Radzen.closePopupOrDialog);
2992                          delete Radzen.dialogService;
2993                          var layout = document.querySelector('.rz-layout');
2994                          if (layout) {
2995                              layout.removeEventListener('keydown', Radzen.disableKeydown);
2996                          }
2997                      }
2998                  });
2999              } catch (e) { }
3000          }
3001      }
3002  },
3003  getNumericValue: function (arg) {
3004    var el =
3005      arg instanceof Element || arg instanceof HTMLDocument
3006        ? arg
3007        : document.getElementById(arg);
3008    return el ? Radzen.getInputValue(el.children[0]) : null;
3009  },
3010  getInputValue: function (arg) {
3011    var input =
3012      arg instanceof Element || arg instanceof HTMLDocument
3013        ? arg
3014        : document.getElementById(arg);
3015    return input && input.value != '' ? input.value : null;
3016  },
3017  setInputValue: function (arg, value) {
3018    var input =
3019      arg instanceof Element || arg instanceof HTMLDocument
3020        ? arg
3021        : document.getElementById(arg);
3022    if (input) {
3023      input.value = value;
3024    }
3025  },
3026  getSelectionRange: function (arg) {
3027    var input =
3028      arg instanceof Element || arg instanceof HTMLDocument
3029        ? arg
3030        : document.getElementById(arg);
3031    if (!input) return null;
3032    try {
3033      if (input.selectionStart === null || input.selectionStart === undefined) return null;
3034      return [input.selectionStart, input.selectionEnd];
3035    } catch (e) {
3036      return null;
3037    }
3038  },
3039  setSelectionRange: function (arg, start, end) {
3040    var input =
3041      arg instanceof Element || arg instanceof HTMLDocument
3042        ? arg
3043        : document.getElementById(arg);
3044    if (!input) return;
3045    var max = input.value ? input.value.length : 0;
3046    var s = Math.max(0, Math.min(start, max));
3047    var e = Math.max(0, Math.min(end, max));
3048    try {
3049      if (typeof input.setSelectionRange === 'function') {
3050        input.setSelectionRange(s, e);
3051      } else {
3052        input.selectionStart = s;
3053        input.selectionEnd = e;
3054      }
3055    } catch (ex) { }
3056  },
3057  isScrolledToBottom: function (arg, threshold) {
3058    var element =
3059      arg instanceof Element || arg instanceof HTMLDocument
3060        ? arg
3061        : document.getElementById(arg);
3062    if (!element) return false;
3063    var offset = typeof threshold === 'number' ? threshold : 0;
3064    return element.scrollTop + element.clientHeight >= element.scrollHeight - offset;
3065  },
3066  blur: function (el, e) { 
3067    if (el) {
3068        e.preventDefault();
3069        el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, keyCode: 9 }));
3070    }
3071  },
3072  readFileAsBase64: function (fileInput, maxFileSize, maxWidth, maxHeight) {
3073    var calculateWidthAndHeight = function (img) {
3074      var width = img.width;
3075      var height = img.height;
3076      // Change the resizing logic
3077      if (width > height) {
3078        if (width > maxWidth) {
3079          height = height * (maxWidth / width);
3080          width = maxWidth;
3081        }
3082      } else {
3083        if (height > maxHeight) {
3084          width = width * (maxHeight / height);
3085          height = maxHeight;
3086        }
3087      }
3088      return { width, height };
3089    };
3090    var readAsDataURL = function (fileInput) {
3091      return new Promise(function (resolve, reject) {
3092        var reader = new FileReader();
3093        reader.onerror = function () {
3094          reader.abort();
3095          reject('Error reading file.');
3096        };
3097        reader.addEventListener(
3098          'load',
3099          function () {
3100            if (fileInput.files[0] && fileInput.files[0].type.includes('image') && maxWidth > 0 && maxHeight > 0) {
3101              var img = document.createElement("img");
3102              img.onload = function (event) {
3103                // Dynamically create a canvas element
3104                var canvas = document.createElement("canvas");
3105                var res = calculateWidthAndHeight(img);
3106                canvas.width = res.width;
3107                canvas.height = res.height;
3108                var ctx = canvas.getContext("2d");
3109                ctx.drawImage(img, 0, 0, res.width, res.height);
3110                resolve(canvas.toDataURL(fileInput.type));
3111              }
3112              img.src = reader.result;
3113            } else {
3114              resolve(reader.result);
3115            }
3116          },
3117          false
3118          );
3119        var file = fileInput.files[0];
3120        if (!file) return;
3121        if (file.size <= maxFileSize) {
3122          reader.readAsDataURL(file);
3123        } else {
3124          reject('File too large.');
3125        }
3126      });
3127    };
3128
3129    return readAsDataURL(fileInput);
3130  },
3131  closeMenuItem: function (item) {
3132    if (!item || !item.classList || !item.classList.contains('rz-navigation-item-active')) {
3133      return;
3134    }
3135
3136    item.classList.remove('rz-navigation-item-active');
3137
3138    var wrapper = item.querySelector('.rz-navigation-item-wrapper');
3139    if (wrapper) {
3140      wrapper.classList.remove('rz-navigation-item-wrapper-active');
3141    }
3142
3143    var children = item.querySelector('.rz-navigation-menu');
3144    if (children) {
3145      item.setAttribute('aria-expanded', false);
3146      children.onanimationend = function () {
3147        children.style.display = 'none';
3148        children.style.insetInlineStart = '';
3149        children.onanimationend = null;
3150      };
3151      children.classList.remove('rz-open');
3152      children.classList.add('rz-close');
3153
3154      children.querySelectorAll('.rz-navigation-item-active').forEach(function (nested) {
3155        Radzen.closeMenuItem(nested);
3156      });
3157    }
3158
3159    var icon = item.querySelector('.rz-navigation-item-icon-children');
3160    if (icon) {
3161      icon.classList.remove('rz-state-expanded');
3162      icon.classList.add('rz-state-collapsed');
3163    }
3164  },
3165  toggleMenuItem: function (target, event, defaultActive, clickToOpen) {
3166    if (!target) return;
3167
3168    var item = target.closest('.rz-navigation-item');
3169
3170    if (!item) return;
3171
3172    var active = defaultActive != undefined ? defaultActive : !item.classList.contains('rz-navigation-item-active');
3173
3174    function toggle(active) {
3175      if (active && item.parentElement) {
3176        Array.prototype.forEach.call(item.parentElement.children, function (sibling) {
3177          if (sibling !== item) {
3178            Radzen.closeMenuItem(sibling);
3179          }
3180        });
3181      }
3182
3183      item.classList.toggle('rz-navigation-item-active', active);
3184
3185      target.classList.toggle('rz-navigation-item-wrapper-active', active);
3186
3187      var children = item.querySelector('.rz-navigation-menu');
3188
3189      if (children) {
3190        item.setAttribute('aria-expanded', active);
3191        if (active) {
3192          children.onanimationend = null;
3193          children.style.display = '';
3194          children.classList.add('rz-open');
3195          children.classList.remove('rz-close');
3196
3197          var isRtl = getComputedStyle(item).direction === 'rtl';
3198          var round = function (value) { return Math.round(value * 1000) / 1000; };
3199          var el = item.parentElement;
3200          var scrollParent = null;
3201          while (el && el !== document.body) {
3202            var s = getComputedStyle(el);
3203            if (s.overflowX === 'auto' || s.overflowX === 'scroll' || s.overflowX === 'hidden') {
3204              scrollParent = el;
3205              break;
3206            }
3207            el = el.parentElement;
3208          }
3209
3210          if (children.hasAttribute('data-flyout')) {
3211            children.removeAttribute('data-flyout-flip');
3212            var childRect = children.getBoundingClientRect();
3213            var itemRect = item.getBoundingClientRect();
3214            if (isRtl) {
3215              var leftBoundary = scrollParent ? scrollParent.getBoundingClientRect().left : 0;
3216              if (round(itemRect.left - childRect.width) < round(leftBoundary)) {
3217                children.setAttribute('data-flyout-flip', '');
3218              }
3219            } else {
3220              var rightBoundary = scrollParent ? scrollParent.getBoundingClientRect().right : document.documentElement.clientWidth;
3221              if (round(itemRect.right + childRect.width) > round(rightBoundary)) {
3222                children.setAttribute('data-flyout-flip', '');
3223              }
3224            }
3225          }
3226
3227          // Shift dropdown left/right if it overflows the scroll parent or viewport
3228          children.style.insetInlineStart = children.style.insetInlineStart || '';
3229          var childRect = children.getBoundingClientRect();
3230          var rightBoundary = scrollParent ? scrollParent.getBoundingClientRect().right : document.documentElement.clientWidth;
3231          var leftBoundary = scrollParent ? scrollParent.getBoundingClientRect().left : 0;
3232          if (!isRtl && round(childRect.right) > round(rightBoundary)) {
3233            var offset = parseFloat(children.style.insetInlineStart) || 0;
3234            children.style.insetInlineStart = (offset + rightBoundary - childRect.right) + 'px';
3235          } else if (isRtl && round(childRect.left) < round(leftBoundary)) {
3236            var offset = parseFloat(children.style.insetInlineStart) || 0;
3237            children.style.insetInlineStart = (offset + leftBoundary - childRect.left) + 'px';
3238          }
3239        } else {
3240          children.onanimationend = function () {
3241            children.style.display = 'none';
3242            children.style.insetInlineStart = '';
3243            children.onanimationend = null;
3244          }
3245          children.classList.remove('rz-open');
3246          children.classList.add('rz-close');
3247        }
3248      }
3249
3250      var icon = item.querySelector('.rz-navigation-item-icon-children');
3251
3252      if (icon) {
3253        icon.classList.toggle('rz-state-expanded', active);
3254        icon.classList.toggle('rz-state-collapsed', !active);
3255      }
3256    }
3257
3258    if (clickToOpen === false && item.parentElement && item.parentElement.closest('.rz-navigation-item') && !defaultActive) {
3259        return;
3260    };
3261
3262    toggle(active);
3263
3264    document.removeEventListener('click', target.clickHandler);
3265
3266    target.clickHandler = function (event) {
3267      if (item.contains(event.target)) {
3268        var child = event.target.closest('.rz-navigation-item');
3269        if (child && child.querySelector('.rz-navigation-menu')) {
3270          return;
3271        }
3272      }
3273      toggle(false);
3274    }
3275
3276    document.addEventListener('click', target.clickHandler);
3277  },
3278  destroyChart: function (ref) {
3279    if(!ref) return;
3280    ref.removeEventListener('mouseleave', ref.mouseLeaveHandler);
3281    delete ref.mouseLeaveHandler;
3282    ref.removeEventListener('mouseenter', ref.mouseEnterHandler);
3283    delete ref.mouseEnterHandler;
3284    ref.removeEventListener('mousemove', ref.mouseMoveHandler);
3285    delete ref.mouseMoveHandler;
3286    ref.removeEventListener('click', ref.clickHandler);
3287    delete ref.clickHandler;
3288    ref.removeEventListener('wheel', ref.wheelHandler);
3289    delete ref.wheelHandler;
3290    if (ref.legendEnterHandler) {
3291      ref.removeEventListener('mouseenter', ref.legendEnterHandler, true);
3292      delete ref.legendEnterHandler;
3293    }
3294    if (ref.legendLeaveHandler) {
3295      ref.removeEventListener('mouseleave', ref.legendLeaveHandler, true);
3296      delete ref.legendLeaveHandler;
3297    }
3298    if (ref.scrollbarMoveHandler) {
3299      document.removeEventListener('mousemove', ref.scrollbarMoveHandler);
3300      delete ref.scrollbarMoveHandler;
3301    }
3302    if (ref.scrollbarUpHandler) {
3303      document.removeEventListener('mouseup', ref.scrollbarUpHandler);
3304      delete ref.scrollbarUpHandler;
3305    }
3306    if (ref.scrollbarTouchMoveHandler) {
3307      document.removeEventListener('touchmove', ref.scrollbarTouchMoveHandler);
3308      delete ref.scrollbarTouchMoveHandler;
3309    }
3310    if (ref.scrollbarTouchEndHandler) {
3311      document.removeEventListener('touchend', ref.scrollbarTouchEndHandler);
3312      delete ref.scrollbarTouchEndHandler;
3313    }
3314    if (ref.touchStartHandler) {
3315      ref.removeEventListener('touchstart', ref.touchStartHandler);
3316      delete ref.touchStartHandler;
3317    }
3318    if (ref.touchMoveHandler) {
3319      ref.removeEventListener('touchmove', ref.touchMoveHandler);
3320      delete ref.touchMoveHandler;
3321    }
3322    if (ref.touchEndHandler) {
3323      ref.removeEventListener('touchend', ref.touchEndHandler);
3324      delete ref.touchEndHandler;
3325    }
3326    if (ref._chartRTLObserver) {
3327      ref._chartRTLObserver.disconnect();
3328      delete ref._chartRTLObserver;
3329    }
3330    this.destroyResizable(ref);
3331  },
3332  createRangeNavigator: function (ref, instance) {
3333    if (!ref) return [0, 0];
3334
3335    var width = ref.offsetWidth;
3336    var height = ref.offsetHeight;
3337
3338    var dragging = null;
3339    var dragStartX = 0;
3340    var dragStartStart = 0;
3341    var dragStartEnd = 0;
3342    var currentStart = 0;
3343    var currentEnd = 1;
3344    var minRange = 0.01;
3345    function isRTL() {
3346      return document.documentElement.dir === 'rtl';
3347    }
3348
3349    function getPositionFromEvent(e) {
3350      var rect = ref.getBoundingClientRect();
3351      var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3352      if (isRTL()) x = rect.width - x;
3353      return Math.max(0, Math.min(1, x / rect.width));
3354    }
3355
3356    function readPositionFromDOM() {
3357      var w = ref.querySelector('.rz-range-nav-window');
3358      if (w) {
3359        var wRect = w.getBoundingClientRect();
3360        var rRect = ref.getBoundingClientRect();
3361        if (rRect.width > 0) {
3362          if (isRTL()) {
3363            currentStart = (rRect.right - wRect.right) / rRect.width;
3364            currentEnd = (rRect.right - wRect.left) / rRect.width;
3365          } else {
3366            currentStart = (wRect.left - rRect.left) / rRect.width;
3367            currentEnd = (wRect.right - rRect.left) / rRect.width;
3368          }
3369        }
3370      }
3371    }
3372
3373    var snapThreshold = 0.01;
3374    function snap(v) { return v < snapThreshold ? 0 : v > 1 - snapThreshold ? 1 : v; }
3375
3376    // Label state — set by Radzen.updateRangeNavigatorLabels
3377    ref.navLabelIsDate = false;
3378    ref.navLabelInputStart = 0;
3379    ref.navLabelInputEnd = 0;
3380
3381    function formatLabel(fraction) {
3382        var inputStart = ref.navLabelInputStart;
3383        var inputEnd = ref.navLabelInputEnd;
3384        if (inputStart === inputEnd) return '';
3385
3386        var value = inputStart + fraction * (inputEnd - inputStart);
3387
3388        if (ref.navLabelIsDate) {
3389            // .NET ticks -> JavaScript Date
3390            var ticksToMs = value / 10000 - 62135596800000;
3391            var d = new Date(ticksToMs);
3392
3393            var format = ref.handleLabelFormatString || "{0:MM/dd/yyyy}";
3394
3395            // Extract the format from "{0:MM/dd/yyyy}"
3396            format = format.replace(/^\{0:/, '').replace(/\}$/, '');
3397
3398            var month = d.getMonth();
3399            var monthNumber = month + 1;
3400            var day = d.getDate();
3401            var hours24 = d.getHours();
3402            var hours12 = hours24 % 12 || 12;
3403
3404            var monthNames = [
3405                'January', 'February', 'March', 'April', 'May', 'June',
3406                'July', 'August', 'September', 'October', 'November', 'December'
3407            ];
3408
3409            var monthNamesShort = [
3410                'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
3411                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
3412            ];
3413
3414            var year = d.getFullYear();
3415
3416            // Single-pass tokenizer. Quoted literals are preserved
3417            return format.replace(
3418                /'([^']*)'|yyyy|yy|MMMM|MMM|MM|M|dd|d|HH|hh|mm|ss|tt/g,
3419                function (match, literal) {
3420                    if (literal !== undefined) {
3421                        return literal;
3422                    }
3423
3424                    switch (match) {
3425                        case 'yyyy':
3426                            return String(year);
3427
3428                        case 'yy':
3429                            return String(year % 100).padStart(2, '0');
3430
3431                        case 'MMMM':
3432                            return monthNames[month];
3433
3434                        case 'MMM':
3435                            return monthNamesShort[month];
3436
3437                        case 'MM':
3438                            return String(monthNumber).padStart(2, '0');
3439
3440                        case 'M':
3441                            return String(monthNumber);
3442
3443                        case 'dd':
3444                            return String(day).padStart(2, '0');
3445
3446                        case 'd':
3447                            return String(day);
3448
3449                        case 'HH':
3450                            return String(hours24).padStart(2, '0');
3451
3452                        case 'hh':
3453                            return String(hours12).padStart(2, '0');
3454
3455                        case 'mm':
3456                            return String(d.getMinutes()).padStart(2, '0');
3457
3458                        case 'ss':
3459                            return String(d.getSeconds()).padStart(2, '0');
3460
3461                        case 'tt':
3462                            return hours24 < 12 ? 'AM' : 'PM';
3463
3464                        default:
3465                            return match;
3466                    }
3467                }
3468            );
3469        }
3470
3471        return Math.round(value).toString();
3472    }
3473
3474    function setLabelText(el, text) {
3475      if (el.firstChild && el.firstChild.nodeType === 3) {
3476        el.firstChild.nodeValue = text;
3477      } else {
3478        el.textContent = text;
3479      }
3480    }
3481
3482    function updateLabels(start, end) {
3483      if (!ref.navLabelInputEnd) return;
3484      var labels = ref.querySelectorAll('.rz-range-nav-label');
3485      if (labels.length >= 2) {
3486        setLabelText(labels[0], formatLabel(start));
3487        setLabelText(labels[1], formatLabel(end));
3488        // Flip label direction when near edges
3489        labels[0].className = 'rz-range-nav-label ' + (start < 0.1 ? 'rz-range-nav-label-end' : 'rz-range-nav-label-start');
3490        labels[1].className = 'rz-range-nav-label ' + (end > 0.9 ? 'rz-range-nav-label-start' : 'rz-range-nav-label-end');
3491      }
3492    }
3493
3494    function updateDOM(start, end) {
3495      currentStart = snap(start);
3496      currentEnd = snap(end);
3497      start = currentStart;
3498      end = currentEnd;
3499      var win = ref.querySelector('.rz-range-nav-window');
3500      var shadeLeft = ref.querySelector('.rz-range-nav-shade-start');
3501      var shadeRight = ref.querySelector('.rz-range-nav-shade-end');
3502      if (win) {
3503        if (isRTL()) {
3504          win.style.right = (start * 100) + '%';
3505          win.style.left = 'auto';
3506        } else {
3507          win.style.left = (start * 100) + '%';
3508          win.style.right = 'auto';
3509        }
3510        win.style.width = ((end - start) * 100) + '%';
3511      }
3512      if (shadeLeft) shadeLeft.style.width = (start * 100) + '%';
3513      if (shadeRight) shadeRight.style.width = ((1 - end) * 100) + '%';
3514      updateLabels(start, end);
3515    }
3516
3517    ref.navTooltipPoints = [];
3518
3519    ref.navHideTooltip = function () {
3520      var tooltip = ref.querySelector('.rz-range-nav-tooltip');
3521      if (tooltip) tooltip.style.display = 'none';
3522    };
3523
3524    function nearestPoint(pos) {
3525      var points = ref.navTooltipPoints;
3526      var lo = 0, hi = points.length - 1;
3527      while (lo < hi) {
3528        var mid = (lo + hi) >> 1;
3529        if (points[mid].x < pos) {
3530          lo = mid + 1;
3531        } else {
3532          hi = mid;
3533        }
3534      }
3535      if (lo > 0 && pos - points[lo - 1].x < points[lo].x - pos) lo--;
3536      return points[lo];
3537    }
3538
3539    function isOverTrack(e) {
3540      var rect = ref.getBoundingClientRect();
3541      return e.clientY <= rect.bottom - (parseFloat(getComputedStyle(ref).paddingBottom) || 0);
3542    }
3543
3544    function clipBounds() {
3545      var bounds = { top: 0, left: 0, right: document.documentElement.clientWidth };
3546      for (var el = ref.parentElement; el && el !== document.body; el = el.parentElement) {
3547        var style = getComputedStyle(el);
3548        var rect = el.getBoundingClientRect();
3549        if (style.overflowY !== 'visible') {
3550          bounds.top = Math.max(bounds.top, rect.top);
3551        }
3552        if (style.overflowX !== 'visible') {
3553          bounds.left = Math.max(bounds.left, rect.left);
3554          bounds.right = Math.min(bounds.right, rect.right);
3555        }
3556        if (style.position === 'fixed') {
3557          break;
3558        }
3559      }
3560      return bounds;
3561    }
3562
3563    function positionTooltip(tooltip, point) {
3564      var track = ref.querySelector('svg');
3565      tooltip.style.insetInlineStart = (point.x * 100) + '%';
3566      tooltip.style.insetBlockStart = (point.y * (track ? track.clientHeight : 0)) + 'px';
3567
3568      var content = tooltip.querySelector('.rz-chart-tooltip-content');
3569      var popup = content.parentElement;
3570      var bounds = clipBounds();
3571      popup.style.left = '';
3572      popup.classList.remove('rz-bottom-chart-tooltip');
3573      popup.classList.add('rz-top-chart-tooltip');
3574      if (content.getBoundingClientRect().top < bounds.top) {
3575        popup.classList.remove('rz-top-chart-tooltip');
3576        popup.classList.add('rz-bottom-chart-tooltip');
3577      }
3578
3579      var rect = popup.getBoundingClientRect();
3580      if (rect.left < bounds.left) {
3581        popup.style.left = (bounds.left - rect.left) + 'px';
3582      } else if (rect.right > bounds.right) {
3583        popup.style.left = (bounds.right - rect.right) + 'px';
3584      }
3585    }
3586
3587    var tooltipPoint = null;
3588
3589    ref.navHover = function (e) {
3590      if (!ref.navTooltipPoints.length) return;
3591
3592      var tooltip = ref.querySelector('.rz-range-nav-tooltip');
3593      if (!tooltip || dragging || !isOverTrack(e)) {
3594        ref.navHideTooltip();
3595        return;
3596      }
3597      var point = nearestPoint(getPositionFromEvent(e));
3598      var wasHidden = tooltip.style.display === 'none';
3599      var content = tooltip.querySelector('.rz-chart-tooltip-content');
3600
3601      if (point !== tooltipPoint || wasHidden) {
3602        tooltipPoint = point;
3603        tooltip.querySelector('.rz-chart-tooltip-title').textContent = point.category;
3604        tooltip.querySelector('.rz-chart-tooltip-item-value').textContent = point.value;
3605        tooltip.style.setProperty('--rz-series-color', point.color || 'currentColor');
3606        content.style.border = '1px solid ' + getComputedStyle(tooltip.querySelector('.rz-active-point-dot')).fill;
3607      }
3608
3609      tooltip.style.display = '';
3610      positionTooltip(tooltip, point);
3611
3612      if (wasHidden) {
3613        content.classList.remove('rz-chart-tooltip-enter');
3614        void content.offsetWidth;
3615        content.classList.add('rz-chart-tooltip-enter');
3616      }
3617    };
3618
3619    function notifyBlazor(start, end) {
3620      instance.invokeMethodAsync('OnNavigatorDrag', snap(start), snap(end)).catch(function (ex) {
3621        console.error('RangeNav invoke error:', ex);
3622      });
3623    }
3624
3625    readPositionFromDOM();
3626
3627    ref.navMouseDown = function (e) {
3628      ref.navHideTooltip();
3629      // Always read fresh position from DOM on mousedown
3630      readPositionFromDOM();
3631
3632      var pos = getPositionFromEvent(e);
3633      var handleZone = 12 / (ref.offsetWidth || 1);
3634      var winRange = currentEnd - currentStart;
3635
3636      if (pos >= currentStart - handleZone && pos <= currentEnd + handleZone) {
3637        var distToLeft = Math.abs(pos - currentStart);
3638        var distToRight = Math.abs(pos - currentEnd);
3639
3640        if (winRange < handleZone * 3) {
3641          dragging = distToLeft <= distToRight ? 'left' : 'right';
3642        } else if (distToLeft <= handleZone) {
3643          dragging = 'left';
3644        } else if (distToRight <= handleZone) {
3645          dragging = 'right';
3646        } else {
3647          dragging = 'pan';
3648        }
3649      } else {
3650        // Click on shade: center selection at click position
3651        var halfRange = winRange / 2;
3652        var newStart = Math.max(0, Math.min(1 - winRange, pos - halfRange));
3653        var newEnd = newStart + winRange;
3654        updateDOM(newStart, newEnd);
3655        notifyBlazor(newStart, newEnd);
3656        return;
3657      }
3658
3659      if (dragging) {
3660        e.preventDefault();
3661        dragStartX = (e.touches ? e.touches[0].clientX : e.clientX);
3662        dragStartStart = currentStart;
3663        dragStartEnd = currentEnd;
3664      }
3665    };
3666
3667    ref.navMouseMove = function (e) {
3668      if (!dragging) return;
3669      e.preventDefault();
3670      var clientX = e.touches ? e.touches[0].clientX : e.clientX;
3671      var dx = clientX - dragStartX;
3672      if (isRTL()) dx = -dx;
3673      var refWidth = ref.offsetWidth;
3674      if (refWidth <= 0) return;
3675      var delta = dx / refWidth;
3676
3677      var newStart = dragStartStart;
3678      var newEnd = dragStartEnd;
3679
3680      if (dragging === 'left') {
3681        newStart = Math.max(0, Math.min(dragStartEnd - minRange, dragStartStart + delta));
3682      } else if (dragging === 'right') {
3683        newEnd = Math.max(dragStartStart + minRange, Math.min(1, dragStartEnd + delta));
3684      } else if (dragging === 'pan') {
3685        var range = dragStartEnd - dragStartStart;
3686        newStart = dragStartStart + delta;
3687        newEnd = newStart + range;
3688        if (newStart < 0) { newStart = 0; newEnd = range; }
3689        if (newEnd > 1) { newEnd = 1; newStart = 1 - range; }
3690      }
3691
3692      updateDOM(newStart, newEnd);
3693    };
3694
3695    ref.navMouseUp = function () {
3696      if (dragging) {
3697        notifyBlazor(currentStart, currentEnd);
3698      }
3699      dragging = null;
3700    };
3701
3702    ref.addEventListener('mousedown', ref.navMouseDown);
3703    ref.addEventListener('touchstart', ref.navMouseDown, { passive: false });
3704    ref.addEventListener('mousemove', ref.navHover);
3705    ref.addEventListener('mouseleave', ref.navHideTooltip);
3706    document.addEventListener('mousemove', ref.navMouseMove);
3707    document.addEventListener('touchmove', ref.navMouseMove, { passive: false });
3708    document.addEventListener('mouseup', ref.navMouseUp);
3709    document.addEventListener('touchend', ref.navMouseUp);
3710
3711    ref.navResizeObserver = new ResizeObserver(function (entries) {
3712      for (var entry of entries) {
3713        var w = entry.contentRect.width;
3714        var h = entry.contentRect.height;
3715        if (w > 0) {
3716          try { suppressDisposed(instance.invokeMethodAsync('OnResize', w, h)); } catch (ex) {}
3717        }
3718      }
3719    });
3720    ref.navResizeObserver.observe(ref);
3721
3722    ref._radzenDispose = function () {
3723      if (ref.navMouseDown) {
3724        ref.removeEventListener('mousedown', ref.navMouseDown);
3725        ref.removeEventListener('touchstart', ref.navMouseDown);
3726        delete ref.navMouseDown;
3727      }
3728      if (ref.navHover) {
3729        ref.removeEventListener('mousemove', ref.navHover);
3730        ref.removeEventListener('mouseleave', ref.navHideTooltip);
3731        delete ref.navHover;
3732        delete ref.navHideTooltip;
3733      }
3734      if (ref.navMouseMove) {
3735        document.removeEventListener('mousemove', ref.navMouseMove);
3736        document.removeEventListener('touchmove', ref.navMouseMove);
3737        delete ref.navMouseMove;
3738      }
3739      if (ref.navMouseUp) {
3740        document.removeEventListener('mouseup', ref.navMouseUp);
3741        document.removeEventListener('touchend', ref.navMouseUp);
3742        delete ref.navMouseUp;
3743      }
3744      if (ref.navResizeObserver) {
3745        ref.navResizeObserver.disconnect();
3746        delete ref.navResizeObserver;
3747      }
3748    };
3749    return [width, height];
3750  },
3751
3752    updateRangeNavigatorLabels: function (ref, isDate, inputStart, inputEnd, handleLabelFormatString) {
3753        if (!ref) return;
3754        ref.navLabelIsDate = isDate;
3755        ref.navLabelInputStart = inputStart;
3756        ref.navLabelInputEnd = inputEnd;
3757        ref.handleLabelFormatString = handleLabelFormatString;
3758    },
3759
3760    updateRangeNavigatorTooltip: function (ref, points) {
3761        if (!ref) return;
3762        ref.navTooltipPoints = points || [];
3763        if (!ref.navTooltipPoints.length && ref.navHideTooltip) ref.navHideTooltip();
3764    },
3765
3766  destroyGauge: function (ref) {
3767    if (ref._gaugeRTLObserver) {
3768      ref._gaugeRTLObserver.disconnect();
3769      delete ref._gaugeRTLObserver;
3770    }
3771    this.destroyResizable(ref);
3772  },
3773  disposeElement: function (ref) {
3774    if (ref && ref._radzenDispose) {
3775      ref._radzenDispose();
3776      delete ref._radzenDispose;
3777    }
3778  },
3779  destroyResizable: function (ref) {
3780    if (ref.resizeObserver) {
3781      ref.resizeObserver.disconnect();
3782      delete ref.resizeObserver;
3783    }
3784    if (ref.resizeHandler) {
3785      window.removeEventListener('resize', ref.resizeHandler);
3786      delete ref.resizeHandler;
3787    }
3788  },
3789  createResizable: function (ref, instance) {
3790    ref.resizeHandler = function () {
3791      var rect = ref.getBoundingClientRect();
3792
3793      try { suppressDisposed(instance.invokeMethodAsync('Resize', rect.width, rect.height)); } catch { }
3794    };
3795
3796    if (window.ResizeObserver) {
3797      ref.resizeObserver = new ResizeObserver(ref.resizeHandler);
3798      ref.resizeObserver.observe(ref);
3799    } else {
3800      window.addEventListener('resize', ref.resizeHandler);
3801    }
3802
3803    var self = this;
3804    ref._radzenDispose = function () { self.destroyResizable(ref); };
3805    var rect = ref.getBoundingClientRect();
3806    return { width: rect.width, height: rect.height };
3807  },
3808  chartToSvg: function (ref) {
3809    var svg = ref && ref.querySelector(':scope > svg');
3810    if (!svg) return '';
3811
3812    var clone = svg.cloneNode(true);
3813    var props = ['fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap',
3814      'stroke-linejoin', 'opacity', 'font-size', 'font-family', 'font-weight', 'font-variant-numeric',
3815      'text-anchor', 'dominant-baseline', 'stop-color', 'stop-opacity'];
3816    var bake = function (source, target) {
3817      var computed = getComputedStyle(source);
3818      var style = '';
3819      for (var i = 0; i < props.length; i++) {
3820        var value = computed.getPropertyValue(props[i]);
3821        if (value && value !== 'none' || props[i] === 'fill' || props[i] === 'stroke') {
3822          style += props[i] + ':' + value + ';';
3823        }
3824      }
3825      target.setAttribute('style', style);
3826    };
3827    var sources = svg.querySelectorAll('*');
3828    var targets = clone.querySelectorAll('*');
3829    for (var i = 0; i < sources.length; i++) {
3830      bake(sources[i], targets[i]);
3831    }
3832
3833    var rect = svg.getBoundingClientRect();
3834    clone.setAttribute('width', rect.width);
3835    clone.setAttribute('height', rect.height);
3836    clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
3837
3838    return new XMLSerializer().serializeToString(clone);
3839  },
3840  svgToPng: async function (svg, width, height) {
3841    if (!svg) return null;
3842
3843    var svgUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
3844    var img = new Image();
3845    await new Promise(function (resolve, reject) {
3846      img.onload = resolve;
3847      img.onerror = reject;
3848      img.src = svgUrl;
3849    });
3850
3851    var naturalWidth = img.naturalWidth || img.width;
3852    var naturalHeight = img.naturalHeight || img.height;
3853    var canvas = document.createElement('canvas');
3854    if (width > 0 || height > 0) {
3855      canvas.width = Math.round(width > 0 ? width : height * naturalWidth / naturalHeight);
3856      canvas.height = Math.round(height > 0 ? height : width * naturalHeight / naturalWidth);
3857    } else {
3858      var scale = Math.max(2, window.devicePixelRatio || 1);
3859      canvas.width = Math.round(naturalWidth * scale);
3860      canvas.height = Math.round(naturalHeight * scale);
3861    }
3862    var ctx = canvas.getContext('2d');
3863    var background = getComputedStyle(document.body).backgroundColor;
3864    if (background && background !== 'rgba(0, 0, 0, 0)') {
3865      ctx.fillStyle = background;
3866      ctx.fillRect(0, 0, canvas.width, canvas.height);
3867    }
3868    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
3869
3870    return await new Promise(function (resolve) {
3871      canvas.toBlob(resolve, 'image/png');
3872    });
3873  },
3874  downloadSvgAsPng: async function (svg, fileName, width, height) {
3875    var blob = await this.svgToPng(svg, width, height);
3876    if (blob) {
3877      this.downloadFile(fileName, blob, 'image/png');
3878    }
3879  },
3880  createChart: function (ref, instance, mouseMoveInterval) {
3881    var inside = false;
3882    var interval = mouseMoveInterval || 0;
3883    var lastDispatch = 0;
3884    var pendingMove = null;
3885    var moveRafId = null;
3886
3887    function dispatchMouseMove() {
3888      moveRafId = null;
3889      if (!pendingMove) return;
3890      var now = performance.now();
3891      if (interval > 0 && now - lastDispatch < interval) {
3892        moveRafId = requestAnimationFrame(dispatchMouseMove);
3893        return;
3894      }
3895      lastDispatch = now;
3896      var move = pendingMove;
3897      pendingMove = null;
3898      try { suppressDisposed(instance.invokeMethodAsync('MouseMove', move.x, move.y)); } catch { }
3899    }
3900
3901    ref.mouseMoveHandler = function (e) {
3902      if (inside) {
3903        var rect = ref.getBoundingClientRect();
3904        pendingMove = { x: e.clientX - rect.left, y: e.clientY - rect.top };
3905        if (!moveRafId) {
3906          moveRafId = requestAnimationFrame(dispatchMouseMove);
3907        }
3908      }
3909    };
3910    ref.mouseEnterHandler = function () {
3911        inside = true;
3912    };
3913    function leave() {
3914      inside = false;
3915      pendingMove = null;
3916      if (moveRafId) {
3917        cancelAnimationFrame(moveRafId);
3918        moveRafId = null;
3919      }
3920      try { suppressDisposed(instance.invokeMethodAsync('MouseMove', -1, -1)); } catch { }
3921    }
3922
3923    ref.mouseLeaveHandler = function (e) {
3924        var tooltip = e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest('.rz-chart-tooltip');
3925        if (tooltip) {
3926            tooltip.addEventListener('mouseleave', function (te) {
3927                if (inside && !(te.relatedTarget && ref.contains(te.relatedTarget))) {
3928                    leave();
3929                }
3930            }, { once: true });
3931            return;
3932        }
3933        leave();
3934    };
3935    ref.clickHandler = function (e) {
3936      var rect = ref.getBoundingClientRect();
3937      var x = e.clientX - rect.left;
3938      var y = e.clientY - rect.top;
3939      if (!e.target.closest('.rz-marker') && !e.target.closest('.rz-legend-item')) {
3940        try { suppressDisposed(instance.invokeMethodAsync('Click', x, y)); } catch { }
3941      }
3942    };
3943
3944    ref.wheelHandler = function (e) {
3945      if (!inside || ref.dataset.allowZoom !== 'true') return;
3946      var delta = e.deltaY > 0 ? 1 : -1;
3947      var range = parseFloat(ref.dataset.zoomEnd) - parseFloat(ref.dataset.zoomStart);
3948      if (isNaN(range)) range = 1;
3949      if (delta < 0 ? range <= 0.01 + 1e-6 : range >= 1 - 1e-6) return;
3950      e.preventDefault();
3951      var rect = ref.getBoundingClientRect();
3952      var x = e.clientX - rect.left;
3953      try { suppressDisposed(instance.invokeMethodAsync('OnWheel', x, delta)); } catch {}
3954    };
3955
3956    ref.addEventListener('mouseenter', ref.mouseEnterHandler);
3957    ref.addEventListener('mouseleave', ref.mouseLeaveHandler);
3958    ref.addEventListener('mousemove', ref.mouseMoveHandler);
3959    ref.addEventListener('click', ref.clickHandler);
3960    ref.addEventListener('wheel', ref.wheelHandler, { passive: false });
3961
3962    // Legend hover → series highlight (only when AllowSeriesHover is enabled)
3963    ref.legendEnterHandler = function (e) {
3964      if (!ref.classList.contains('rz-chart-series-hover')) return;
3965      var legendItem = e.target.closest('.rz-legend-item');
3966      if (!legendItem) return;
3967      var g = legendItem.querySelector('g[class]');
3968      if (!g) return;
3969      var classes = g.className.baseVal || g.getAttribute('class') || '';
3970      var match = classes.match(/rz-series-item-\d+|rz-series-\d+/);
3971      if (!match) return;
3972      var selector = '.' + match[0];
3973      var svg = ref.querySelector('svg');
3974      if (!svg) return;
3975      var targets = svg.querySelectorAll(selector);
3976      targets.forEach(function (t) { t.classList.add('rz-state-hover'); });
3977    };
3978    ref.legendLeaveHandler = function (e) {
3979      if (!ref.classList.contains('rz-chart-series-hover')) return;
3980      var legendItem = e.target.closest('.rz-legend-item');
3981      if (!legendItem) return;
3982      var svg = ref.querySelector('svg');
3983      if (!svg) return;
3984      var hovered = svg.querySelectorAll('.rz-state-hover');
3985      hovered.forEach(function (t) { t.classList.remove('rz-state-hover'); });
3986    };
3987    ref.addEventListener('mouseenter', ref.legendEnterHandler, true);
3988    ref.addEventListener('mouseleave', ref.legendLeaveHandler, true);
3989
3990    // Scrollbar drag handling via event delegation (survives Blazor re-renders)
3991    var dragging = false;
3992    var dragStartX = 0;
3993    var dragStartPos = 0;
3994
3995    function isChartRTL() {
3996      return document.documentElement.dir === 'rtl';
3997    }
3998
3999    function getThumbPos(thumb) {
4000      return parseFloat(isChartRTL() ? thumb.style.right : thumb.style.left) || 0;
4001    }
4002
4003    function setThumbPos(thumb, pos) {
4004      if (isChartRTL()) {
4005        thumb.style.right = pos + '%';
4006        thumb.style.left = '';
4007      } else {
4008        thumb.style.left = pos + '%';
4009        thumb.style.right = '';
4010      }
4011    }
4012
4013    ref.addEventListener('mousedown', function (e) {
4014      var thumb = e.target.closest('.rz-chart-scrollbar-thumb');
4015      if (!thumb) return;
4016      dragging = true;
4017      dragStartX = e.clientX;
4018      dragStartPos = getThumbPos(thumb);
4019      e.preventDefault();
4020    });
4021
4022    ref.scrollbarMoveHandler = function (e) {
4023      if (!dragging) return;
4024      var track = ref.querySelector('.rz-chart-scrollbar-track');
4025      var thumb = ref.querySelector('.rz-chart-scrollbar-thumb');
4026      if (!track || !thumb) return;
4027      var trackWidth = track.offsetWidth;
4028      if (trackWidth <= 0) return;
4029      var dx = e.clientX - dragStartX;
4030      var deltaPercent = (isChartRTL() ? -dx : dx) / trackWidth * 100;
4031      var newPos = Math.max(0, Math.min(100 - parseFloat(thumb.style.width), dragStartPos + deltaPercent));
4032      setThumbPos(thumb, newPos);
4033      var position = newPos / 100;
4034      try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4035    };
4036    document.addEventListener('mousemove', ref.scrollbarMoveHandler);
4037
4038    ref.scrollbarUpHandler = function () {
4039      dragging = false;
4040    };
4041    document.addEventListener('mouseup', ref.scrollbarUpHandler);
4042
4043    ref.addEventListener('click', function (e) {
4044      var track = e.target.closest('.rz-chart-scrollbar-track');
4045      if (!track || e.target.closest('.rz-chart-scrollbar-thumb')) return;
4046      var thumb = track.querySelector('.rz-chart-scrollbar-thumb');
4047      if (!thumb) return;
4048      var trackRect = track.getBoundingClientRect();
4049      var clickX = isChartRTL() ? trackRect.right - e.clientX : e.clientX - trackRect.left;
4050      var thumbWidth = thumb.offsetWidth;
4051      var position = Math.max(0, Math.min(1 - thumbWidth / track.offsetWidth, (clickX - thumbWidth / 2) / track.offsetWidth));
4052      try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4053    });
4054
4055    // Touch support for scrollbar drag
4056    ref.addEventListener('touchstart', function (e) {
4057      var thumb = e.target.closest('.rz-chart-scrollbar-thumb');
4058      if (!thumb) return;
4059      dragging = true;
4060      dragStartX = e.touches[0].clientX;
4061      dragStartPos = getThumbPos(thumb);
4062      e.preventDefault();
4063    }, { passive: false });
4064
4065    ref.scrollbarTouchMoveHandler = function (e) {
4066      if (!dragging) return;
4067      var track = ref.querySelector('.rz-chart-scrollbar-track');
4068      var thumb = ref.querySelector('.rz-chart-scrollbar-thumb');
4069      if (!track || !thumb) return;
4070      var trackWidth = track.offsetWidth;
4071      if (trackWidth <= 0) return;
4072      var dx = e.touches[0].clientX - dragStartX;
4073      var deltaPercent = (isChartRTL() ? -dx : dx) / trackWidth * 100;
4074      var newPos = Math.max(0, Math.min(100 - parseFloat(thumb.style.width), dragStartPos + deltaPercent));
4075      setThumbPos(thumb, newPos);
4076      var position = newPos / 100;
4077      try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4078      e.preventDefault();
4079    };
4080    document.addEventListener('touchmove', ref.scrollbarTouchMoveHandler, { passive: false });
4081
4082    ref.scrollbarTouchEndHandler = function () {
4083      dragging = false;
4084    };
4085    document.addEventListener('touchend', ref.scrollbarTouchEndHandler);
4086
4087    // Touch pan and pinch-to-zoom support
4088    var pinchStartDistance = 0;
4089    var pinchActive = false;
4090    var touchPanning = false;
4091    var touchPanStartX = 0;
4092
4093    ref.touchStartHandler = function (e) {
4094      if (e.touches.length === 2) {
4095        // Two-finger: pinch-to-zoom
4096        pinchActive = true;
4097        touchPanning = false;
4098        var dx = e.touches[0].clientX - e.touches[1].clientX;
4099        var dy = e.touches[0].clientY - e.touches[1].clientY;
4100        pinchStartDistance = Math.sqrt(dx * dx + dy * dy);
4101        e.preventDefault();
4102      } else if (e.touches.length === 1 && !e.target.closest('.rz-chart-scrollbar')) {
4103        // Single-finger on chart area: pan
4104        touchPanning = true;
4105        touchPanStartX = e.touches[0].clientX;
4106      }
4107    };
4108    ref.addEventListener('touchstart', ref.touchStartHandler, { passive: false });
4109
4110    ref.touchMoveHandler = function (e) {
4111      if (pinchActive && e.touches.length === 2) {
4112        // Pinch-to-zoom
4113        var dx = e.touches[0].clientX - e.touches[1].clientX;
4114        var dy = e.touches[0].clientY - e.touches[1].clientY;
4115        var currentDistance = Math.sqrt(dx * dx + dy * dy);
4116        if (pinchStartDistance <= 0) return;
4117        var midX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
4118        var rect = ref.getBoundingClientRect();
4119        var x = midX - rect.left;
4120        var ratio = currentDistance / pinchStartDistance;
4121        if (ratio > 1.05) {
4122          try { suppressDisposed(instance.invokeMethodAsync('OnWheel', x, -1)); } catch {}
4123          pinchStartDistance = currentDistance;
4124        } else if (ratio < 0.95) {
4125          try { suppressDisposed(instance.invokeMethodAsync('OnWheel', x, 1)); } catch {}
4126          pinchStartDistance = currentDistance;
4127        }
4128        e.preventDefault();
4129      } else if (touchPanning && e.touches.length === 1) {
4130        // Single-finger pan
4131        var rect = ref.getBoundingClientRect();
4132        var chartWidth = rect.width;
4133        if (chartWidth <= 0) return;
4134        var dx = e.touches[0].clientX - touchPanStartX;
4135        var thumb = ref.querySelector('.rz-chart-scrollbar-thumb');
4136        if (!thumb) return;
4137        var thumbWidth = parseFloat(thumb.style.width) || 50;
4138        var currentPos = getThumbPos(thumb);
4139        // Swipe in reading direction = move view backward, swipe against = move view forward
4140        var deltaPercent = (isChartRTL() ? dx : -dx) / chartWidth * 100;
4141        var newPos = Math.max(0, Math.min(100 - thumbWidth, currentPos + deltaPercent));
4142        var position = newPos / 100;
4143        try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4144        touchPanStartX = e.touches[0].clientX;
4145        e.preventDefault();
4146      }
4147    };
4148    ref.addEventListener('touchmove', ref.touchMoveHandler, { passive: false });
4149
4150    ref.touchEndHandler = function (e) {
4151      if (e.touches.length < 2) {
4152        pinchActive = false;
4153      }
4154      if (e.touches.length === 0) {
4155        touchPanning = false;
4156      }
4157    };
4158    ref.addEventListener('touchend', ref.touchEndHandler);
4159
4160    // RTL detection
4161    ref._chartRTLObserver = new MutationObserver(function () {
4162      try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4163    });
4164    ref._chartRTLObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });
4165    try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4166
4167    this.createResizable(ref, instance);
4168    var self = this;
4169    ref._radzenDispose = function () { self.destroyChart(ref); };
4170    var rect = ref.getBoundingClientRect();
4171    return { width: rect.width, height: rect.height };
4172  },
4173  createGauge: function (ref, instance) {
4174    this.createResizable(ref, instance);
4175    ref._gaugeRTLObserver = new MutationObserver(function () {
4176      try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4177    });
4178    ref._gaugeRTLObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });
4179    try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4180    var rect = ref.getBoundingClientRect();
4181    return { width: rect.width, height: rect.height };
4182  },
4183  innerHTML: function (ref, value) {
4184    if (value != undefined) {
4185        if (ref != null) {
4186            ref.innerHTML = value;
4187        }
4188    } else {
4189      return ref.innerHTML;
4190    }
4191  },
4192  execCommand: function (ref, name, value) {
4193    if (document.activeElement != ref && ref) {
4194      ref.focus();
4195    }
4196    document.execCommand(name, false, value);
4197    return this.queryCommands(ref);
4198  },
4199  formatTableColumnHeader: function (ref, index) {
4200    var format = (ref && ref.tableColumnHeaderFormat) || 'Column {0}';
4201    return format.replace('{0}', (index + 1).toString());
4202  },
4203  createTableHTML: function (ref, options) {
4204    options = options || {};
4205    var rows = Math.max(parseInt(options.rows || 0, 10) || 0, 1);
4206    var columns = Math.max(parseInt(options.columns || 0, 10) || 0, 1);
4207    var width = options.width || '100%';
4208    var border = Math.max(parseInt(options.border || 0, 10) || 0, 0);
4209    var headerRow = !!options.headerRow;
4210    var html = '<table style="border-collapse: collapse; width: ' + width + ';" border="' + border + '">';
4211
4212    if (headerRow) {
4213      html += '<thead><tr>';
4214      for (var headerIndex = 0; headerIndex < columns; headerIndex++) {
4215        html += '<th>' + this.formatTableColumnHeader(ref, headerIndex) + '</th>';
4216      }
4217      html += '</tr></thead>';
4218    }
4219
4220    html += '<tbody>';
4221    for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
4222      html += '<tr>';
4223      for (var columnIndex = 0; columnIndex < columns; columnIndex++) {
4224        html += '<td>&nbsp;</td>';
4225      }
4226      html += '</tr>';
4227    }
4228    html += '</tbody></table><p></p>';
4229
4230    return html;
4231  },
4232  buildTableContext: function (ref, element) {
4233    if (!ref) {
4234      return { ref: ref };
4235    }
4236
4237    if (!element || !ref.contains(element)) {
4238      return { ref: ref };
4239    }
4240
4241    var cell = element.closest('td,th');
4242    var table = element.closest('table');
4243
4244    if (!cell || !table || !ref.contains(table)) {
4245      return { ref: ref, table: table };
4246    }
4247
4248    var row = cell.parentElement;
4249    var tbody = table.tBodies && table.tBodies.length ? table.tBodies[0] : null;
4250    var thead = table.tHead;
4251    var rowIndex = -1;
4252
4253    if (tbody && tbody.contains(row)) {
4254      rowIndex = Array.prototype.indexOf.call(tbody.rows, row);
4255    }
4256
4257    return {
4258      ref: ref,
4259      table: table,
4260      cell: cell,
4261      row: row,
4262      tbody: tbody,
4263      thead: thead,
4264      inHeader: !!(thead && thead.contains(cell)),
4265      rowIndex: rowIndex,
4266      columnIndex: Array.prototype.indexOf.call(row.children, cell)
4267    };
4268  },
4269  getTableContext: function (ref) {
4270    var selection = getSelection();
4271    var range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
4272    var node = selection && selection.focusNode ? selection.focusNode : range && range.commonAncestorContainer;
4273
4274    if (!node) {
4275      return { ref: ref };
4276    }
4277
4278    return this.buildTableContext(ref, node.nodeType === 3 ? node.parentElement : node);
4279  },
4280  getCellMatrix: function (section) {
4281    var matrix = [];
4282    if (!section || !section.rows) {
4283      return matrix;
4284    }
4285
4286    for (var rowIndex = 0; rowIndex < section.rows.length; rowIndex++) {
4287      var row = section.rows[rowIndex];
4288      if (!matrix[rowIndex]) {
4289        matrix[rowIndex] = [];
4290      }
4291
4292      var columnIndex = 0;
4293      for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
4294        var cell = row.cells[cellIndex];
4295        while (matrix[rowIndex][columnIndex]) {
4296          columnIndex++;
4297        }
4298
4299        var colSpan = Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1);
4300        var rowSpan = Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1);
4301
4302        for (var r = 0; r < rowSpan; r++) {
4303          var targetRow = rowIndex + r;
4304          if (!matrix[targetRow]) {
4305            matrix[targetRow] = [];
4306          }
4307
4308          for (var c = 0; c < colSpan; c++) {
4309            matrix[targetRow][columnIndex + c] = cell;
4310          }
4311        }
4312
4313        columnIndex += colSpan;
4314      }
4315    }
4316
4317    return matrix;
4318  },
4319  getCellPosition: function (section, cell, matrix) {
4320    matrix = matrix || this.getCellMatrix(section);
4321
4322    for (var rowIndex = 0; rowIndex < matrix.length; rowIndex++) {
4323      for (var columnIndex = 0; columnIndex < (matrix[rowIndex] || []).length; columnIndex++) {
4324        if (matrix[rowIndex][columnIndex] === cell) {
4325          return { rowIndex: rowIndex, columnIndex: columnIndex };
4326        }
4327      }
4328    }
4329
4330    return { rowIndex: -1, columnIndex: -1 };
4331  },
4332  clearSelectedTableCells: function (ref) {
4333    if (!ref) {
4334      return;
4335    }
4336
4337    Array.prototype.forEach.call(ref.querySelectorAll('td.rz-html-editor-cell-selected,th.rz-html-editor-cell-selected'), function (cell) {
4338      cell.classList.remove('rz-html-editor-cell-selected');
4339    });
4340  },
4341  getSelectedTableCells: function (ref) {
4342    if (!ref) {
4343      return [];
4344    }
4345
4346    return Array.prototype.filter.call(ref.querySelectorAll('td.rz-html-editor-cell-selected,th.rz-html-editor-cell-selected'), function (cell) {
4347      return ref.contains(cell);
4348    });
4349  },
4350  selectTableCellRange: function (ref, startCell, endCell) {
4351    if (!ref || !startCell || !endCell) {
4352      return false;
4353    }
4354
4355    var startSection = startCell.parentElement ? startCell.parentElement.parentElement : null;
4356    var endSection = endCell.parentElement ? endCell.parentElement.parentElement : null;
4357
4358    if (!startSection || startSection !== endSection) {
4359      return false;
4360    }
4361
4362    var matrix = this.getCellMatrix(startSection);
4363    var start = this.getCellPosition(startSection, startCell, matrix);
4364    var end = this.getCellPosition(startSection, endCell, matrix);
4365
4366    if (start.rowIndex < 0 || start.columnIndex < 0 || end.rowIndex < 0 || end.columnIndex < 0) {
4367      return false;
4368    }
4369
4370    var minRow = Math.min(start.rowIndex, end.rowIndex);
4371    var maxRow = Math.max(start.rowIndex, end.rowIndex);
4372    var minColumn = Math.min(start.columnIndex, end.columnIndex);
4373    var maxColumn = Math.max(start.columnIndex, end.columnIndex);
4374    this.clearSelectedTableCells(ref);
4375
4376    for (var rowIndex = minRow; rowIndex <= maxRow; rowIndex++) {
4377      for (var columnIndex = minColumn; columnIndex <= maxColumn; columnIndex++) {
4378        var cell = (matrix[rowIndex] || [])[columnIndex];
4379        if (cell) {
4380          cell.classList.add('rz-html-editor-cell-selected');
4381        }
4382      }
4383    }
4384
4385    return true;
4386  },
4387  getSelectedCellBounds: function (ref, section) {
4388    var selectedCells = this.getSelectedTableCells(ref);
4389    if (!selectedCells.length) {
4390      return null;
4391    }
4392
4393    var matrix = this.getCellMatrix(section);
4394    var minRow = Number.MAX_SAFE_INTEGER;
4395    var maxRow = -1;
4396    var minColumn = Number.MAX_SAFE_INTEGER;
4397    var maxColumn = -1;
4398
4399    selectedCells.forEach(function (selectedCell) {
4400      var position = Radzen.getCellPosition(section, selectedCell, matrix);
4401      var rowIndex = position.rowIndex;
4402      var columnIndex = position.columnIndex;
4403      if (rowIndex < 0 || columnIndex < 0) {
4404        return;
4405      }
4406
4407      var rowSpan = Math.max(parseInt(selectedCell.getAttribute('rowspan') || 1, 10) || 1, 1);
4408      var colSpan = Math.max(parseInt(selectedCell.getAttribute('colspan') || 1, 10) || 1, 1);
4409
4410      minRow = Math.min(minRow, rowIndex);
4411      maxRow = Math.max(maxRow, rowIndex + rowSpan - 1);
4412      minColumn = Math.min(minColumn, columnIndex);
4413      maxColumn = Math.max(maxColumn, columnIndex + colSpan - 1);
4414    });
4415
4416    if (minRow === Number.MAX_SAFE_INTEGER || minColumn === Number.MAX_SAFE_INTEGER) {
4417      return null;
4418    }
4419
4420    for (var rowIndex = minRow; rowIndex <= maxRow; rowIndex++) {
4421      for (var columnIndex = minColumn; columnIndex <= maxColumn; columnIndex++) {
4422        var candidate = (matrix[rowIndex] || [])[columnIndex];
4423        if (!candidate || selectedCells.indexOf(candidate) === -1) {
4424          return null;
4425        }
4426      }
4427    }
4428
4429    return {
4430      selectedCells: selectedCells,
4431      matrix: matrix,
4432      minRow: minRow,
4433      maxRow: maxRow,
4434      minColumn: minColumn,
4435      maxColumn: maxColumn
4436    };
4437  },
4438  copySelectedTableCells: function (ref) {
4439    var cells = this.getSelectedTableCells(ref);
4440    if (!cells.length) {
4441      return false;
4442    }
4443
4444    var section = cells[0].parentElement ? cells[0].parentElement.parentElement : null;
4445    var bounds = this.getSelectedCellBounds(ref, section);
4446    if (!bounds) {
4447      return false;
4448    }
4449
4450    var copiedMatrix = [];
4451    for (var rowIndex = bounds.minRow; rowIndex <= bounds.maxRow; rowIndex++) {
4452      copiedMatrix.push([]);
4453      for (var columnIndex = bounds.minColumn; columnIndex <= bounds.maxColumn; columnIndex++) {
4454        var cell = (bounds.matrix[rowIndex] || [])[columnIndex];
4455        copiedMatrix[copiedMatrix.length - 1].push(bounds.selectedCells.indexOf(cell) > -1 && cell ? cell.outerHTML : null);
4456      }
4457    }
4458
4459    ref.copiedCells = {
4460      matrix: copiedMatrix,
4461      rows: copiedMatrix.length,
4462      columns: copiedMatrix.length ? copiedMatrix[0].length : 0
4463    };
4464
4465    return true;
4466  },
4467  pasteSelectedTableCells: function (ref, targetCell) {
4468    if (!ref || !ref.copiedCells || !ref.copiedCells.matrix || !targetCell || !targetCell.parentElement) {
4469      return false;
4470    }
4471
4472    var section = targetCell.parentElement.parentElement;
4473    var matrix = this.getCellMatrix(section);
4474    var position = this.getCellPosition(section, targetCell, matrix);
4475    var startRow = position.rowIndex;
4476    var startColumn = position.columnIndex;
4477
4478    if (startRow < 0 || startColumn < 0) {
4479      return false;
4480    }
4481
4482    for (var guardRowOffset = 0; guardRowOffset < ref.copiedCells.rows; guardRowOffset++) {
4483      for (var guardColumnOffset = 0; guardColumnOffset < ref.copiedCells.columns; guardColumnOffset++) {
4484        var guardCell = (matrix[startRow + guardRowOffset] || [])[startColumn + guardColumnOffset];
4485        if (guardCell) {
4486          var guardCellPosition = this.getCellPosition(section, guardCell, matrix);
4487          var guardRowSpan = Math.max(parseInt(guardCell.getAttribute('rowspan') || 1, 10) || 1, 1);
4488          var guardColSpan = Math.max(parseInt(guardCell.getAttribute('colspan') || 1, 10) || 1, 1);
4489          if ((guardRowSpan > 1 || guardColSpan > 1) && (guardCellPosition.rowIndex !== startRow + guardRowOffset || guardCellPosition.columnIndex !== startColumn + guardColumnOffset)) {
4490            return false;
4491          }
4492        }
4493      }
4494    }
4495
4496    for (var rowOffset = 0; rowOffset < ref.copiedCells.rows; rowOffset++) {
4497      var targetRow = section.rows[startRow + rowOffset];
4498      if (!targetRow) {
4499        if (section.tagName === 'TBODY') {
4500          targetRow = section.insertRow();
4501        } else {
4502          return false;
4503        }
4504      }
4505
4506      for (var columnOffset = 0; columnOffset < ref.copiedCells.columns; columnOffset++) {
4507        var target = (this.getCellMatrix(section)[startRow + rowOffset] || [])[startColumn + columnOffset];
4508        if (!target) {
4509          target = document.createElement(targetCell.tagName.toLowerCase());
4510          target.innerHTML = '&nbsp;';
4511          targetRow.appendChild(target);
4512        }
4513
4514        var html = ref.copiedCells.matrix[rowOffset][columnOffset];
4515        if (html) {
4516          var template = document.createElement('template');
4517          template.innerHTML = html.trim();
4518          var sourceCell = template.content.firstElementChild;
4519          if (sourceCell) {
4520            target.innerHTML = sourceCell.innerHTML;
4521          }
4522        }
4523      }
4524    }
4525
4526    return true;
4527  },
4528  createCommandResult: function (ref, success, message) {
4529    var result = this.queryCommands(ref);
4530    result.success = success;
4531    result.message = message || null;
4532    return result;
4533  },
4534  getContextTableSelection: function (ref) {
4535    var context = ref && ref.contextMenuTarget ? this.getTableContextFromTarget(ref, ref.contextMenuTarget) : this.getTableContext(ref);
4536    if (!context || !context.table) {
4537      return this.getTableSelection(ref);
4538    }
4539
4540    return this.getTableSelectionFromContext(ref, context);
4541  },
4542  getTableContextFromTarget: function (ref, target) {
4543    if (!target) {
4544      return this.getTableContext(ref);
4545    }
4546
4547    return this.buildTableContext(ref, target.nodeType === 3 ? target.parentElement : target);
4548  },
4549  getTableSelectionFromContext: function (ref, context) {
4550    var table = context.table;
4551
4552    if (!table) {
4553      return this.getTableSelection(ref);
4554    }
4555
4556    var cell = context.cell;
4557    var section = context.inHeader && table.tHead ? table.tHead : (context.row ? context.row.parentElement : (table.tBodies[0] || table));
4558    var matrix = this.getCellMatrix(section);
4559    var position = cell ? this.getCellPosition(section, cell, matrix) : { rowIndex: -1, columnIndex: context.columnIndex };
4560    var rowIndex = position.rowIndex;
4561    var columnIndex = position.columnIndex;
4562    var colSpan = cell ? Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1) : 1;
4563    var rowSpan = cell ? Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1) : 1;
4564    var rowCells = matrix[rowIndex] || [];
4565    var rightCell = rowCells[columnIndex + colSpan];
4566    var belowRow = matrix[rowIndex + rowSpan] || [];
4567    var canMergeDown = true;
4568
4569    for (var i = 0; i < colSpan; i++) {
4570      if (!belowRow[columnIndex + i]) {
4571        canMergeDown = false;
4572        break;
4573      }
4574    }
4575
4576    var bodyRows = table.tBodies && table.tBodies.length ? table.tBodies[0].rows.length : 0;
4577    var firstRow = table.rows && table.rows.length ? table.rows[0] : null;
4578    var border = parseInt(table.getAttribute('border') || 0, 10) || 0;
4579    var columnWidth = cell && cell.style ? cell.style.width || null : null;
4580    var cellBackground = cell && cell.style ? cell.style.backgroundColor || null : null;
4581    var cellPadding = cell && cell.style ? cell.style.padding || null : null;
4582    var cellTextAlign = cell && cell.style ? cell.style.textAlign || null : null;
4583    var cellVerticalAlign = cell && cell.style ? cell.style.verticalAlign || null : null;
4584    var cellBorder = cell && cell.style ? cell.style.border || null : null;
4585    var columnWidthPx = columnWidth ? parseInt(columnWidth, 10) || null : null;
4586    var cellPaddingPx = cellPadding ? parseInt(cellPadding, 10) || null : null;
4587    var borderTop = !!(cell && cell.style && cell.style.borderTop !== 'none' && cell.style.borderTop !== '');
4588    var borderRight = !!(cell && cell.style && cell.style.borderRight !== 'none' && cell.style.borderRight !== '');
4589    var borderBottom = !!(cell && cell.style && cell.style.borderBottom !== 'none' && cell.style.borderBottom !== '');
4590    var borderLeft = !!(cell && cell.style && cell.style.borderLeft !== 'none' && cell.style.borderLeft !== '');
4591    var borderStyle = cell && cell.style ? (cell.style.borderStyle || (cellBorder ? cellBorder.split(' ')[1] : null)) : null;
4592    var borderWidthPx = cell && cell.style && cell.style.borderWidth ? parseInt(cell.style.borderWidth, 10) || null : (cellBorder ? parseInt(cellBorder, 10) || null : null);
4593    var borderColor = cell && cell.style ? (cell.style.borderColor || null) : null;
4594
4595    return {
4596      inTable: true,
4597      inHeader: !!context.inHeader,
4598      rowIndex: context.rowIndex,
4599      columnIndex: columnIndex,
4600      rows: bodyRows,
4601      columns: firstRow ? firstRow.children.length : 0,
4602      width: table.style.width || table.getAttribute('width') || '100%',
4603      border: border,
4604      headerRow: !!(table.tHead && table.tHead.rows.length),
4605      colSpan: colSpan,
4606      rowSpan: rowSpan,
4607      canMergeRight: !!rightCell && rightCell !== cell,
4608      canMergeDown: canMergeDown,
4609      canSplit: colSpan > 1 || rowSpan > 1,
4610      columnWidth: columnWidth,
4611      cellBackground: cellBackground,
4612      cellPadding: cellPadding,
4613      cellTextAlign: cellTextAlign,
4614      cellVerticalAlign: cellVerticalAlign,
4615      cellBorder: cellBorder,
4616      columnWidthPx: columnWidthPx,
4617      cellPaddingPx: cellPaddingPx,
4618      borderStyle: borderStyle,
4619      borderWidthPx: borderWidthPx,
4620      borderColor: borderColor,
4621      borderTop: borderTop,
4622      borderRight: borderRight,
4623      borderBottom: borderBottom,
4624      borderLeft: borderLeft
4625    };
4626  },
4627  getTableSelection: function (ref) {
4628    var context = this.getTableContext(ref);
4629    if (!context.table) {
4630      return {
4631        inTable: false,
4632        inHeader: false,
4633        rowIndex: -1,
4634        columnIndex: -1,
4635        rows: 0,
4636        columns: 0,
4637        width: null,
4638        border: 0,
4639        headerRow: false,
4640        colSpan: 1,
4641        rowSpan: 1,
4642        canMergeRight: false,
4643        canMergeDown: false,
4644        canSplit: false,
4645        columnWidth: null,
4646        columnWidthPx: null,
4647        cellBackground: null,
4648        cellPadding: null,
4649        cellPaddingPx: null,
4650        cellTextAlign: null,
4651        cellVerticalAlign: null,
4652        cellBorder: null,
4653        borderStyle: null,
4654        borderWidthPx: null,
4655        borderColor: null,
4656        borderTop: false,
4657        borderRight: false,
4658        borderBottom: false,
4659        borderLeft: false
4660      };
4661    }
4662
4663    return this.getTableSelectionFromContext(ref, context);
4664  },
4665  ensureTableBody: function (table) {
4666    if (!table.tBodies.length) {
4667      table.appendChild(document.createElement('tbody'));
4668    }
4669    return table.tBodies[0];
4670  },
4671  getTableColumnCount: function (table) {
4672    var firstRow = table.rows.length ? table.rows[0] : null;
4673    return firstRow ? firstRow.children.length : 0;
4674  },
4675  createTableRow: function (ref, columns, header) {
4676    var tr = document.createElement('tr');
4677    for (var i = 0; i < columns; i++) {
4678      var element = document.createElement(header ? 'th' : 'td');
4679      element.innerHTML = header ? this.formatTableColumnHeader(ref, i) : '&nbsp;';
4680      tr.appendChild(element);
4681    }
4682    return tr;
4683  },
4684  updateTable: function (context, value) {
4685    value = value || {};
4686    var table = context.table;
4687    var cell = context.cell;
4688    var row = context.row;
4689    var columns = Math.max(parseInt(value.columns || 0, 10) || 0, 1);
4690    var rows = Math.max(parseInt(value.rows || 0, 10) || 0, 1);
4691    var border = Math.max(parseInt(value.border || 0, 10) || 0, 0);
4692    var columnWidth = value.columnWidthPx ? value.columnWidthPx + 'px' : (value.columnWidth || '');
4693    var cellPadding = value.cellPaddingPx != null ? value.cellPaddingPx + 'px' : (value.cellPadding || '');
4694    var borderStyle = value.borderStyle || 'solid';
4695    var borderWidth = value.borderWidthPx != null ? value.borderWidthPx + 'px' : '1px';
4696    var borderColor = value.borderColor || 'currentColor';
4697
4698    table.style.width = value.width || '100%';
4699    if (!table.style.borderCollapse) {
4700      table.style.borderCollapse = 'collapse';
4701    }
4702    table.setAttribute('border', border.toString());
4703
4704    this.updateTableHeader(context.ref, table, columns, !!value.headerRow);
4705    this.updateTableBody(context.ref, table, rows, columns);
4706
4707    if (cell) {
4708      this.updateTableCellStyles(row, cell, context, {
4709        columnWidth: columnWidth,
4710        cellBackground: value.cellBackground || '',
4711        cellPadding: cellPadding,
4712        cellTextAlign: value.cellTextAlign || '',
4713        cellVerticalAlign: value.cellVerticalAlign || '',
4714        cellBorder: value.cellBorder || '',
4715        borderValue: borderWidth + ' ' + borderStyle + ' ' + borderColor,
4716        borderTop: value.borderTop,
4717        borderRight: value.borderRight,
4718        borderBottom: value.borderBottom,
4719        borderLeft: value.borderLeft
4720      });
4721    }
4722  },
4723  updateTableHeader: function (ref, table, columns, headerRow) {
4724    if (headerRow) {
4725      var thead = table.tHead || table.createTHead();
4726      var header = thead.rows.length ? thead.rows[0] : thead.insertRow();
4727      while (header.children.length < columns) {
4728        var th = document.createElement('th');
4729        th.innerHTML = this.formatTableColumnHeader(ref, header.children.length);
4730        header.appendChild(th);
4731      }
4732      while (header.children.length > columns) {
4733        header.removeChild(header.lastElementChild);
4734      }
4735      for (var h = 0; h < header.children.length; h++) {
4736        if (!header.children[h].innerHTML || header.children[h].innerHTML === '&nbsp;') {
4737          header.children[h].innerHTML = this.formatTableColumnHeader(ref, h);
4738        }
4739      }
4740    } else if (table.tHead) {
4741      table.deleteTHead();
4742    }
4743  },
4744  updateTableBody: function (ref, table, rows, columns) {
4745    var tbody = this.ensureTableBody(table);
4746    while (tbody.rows.length < rows) {
4747      tbody.appendChild(this.createTableRow(ref, columns, false));
4748    }
4749    while (tbody.rows.length > rows) {
4750      tbody.removeChild(tbody.lastElementChild);
4751    }
4752    for (var r = 0; r < tbody.rows.length; r++) {
4753      var bodyRow = tbody.rows[r];
4754      while (bodyRow.children.length < columns) {
4755        var td = document.createElement('td');
4756        td.innerHTML = '&nbsp;';
4757        bodyRow.appendChild(td);
4758      }
4759      while (bodyRow.children.length > columns) {
4760        bodyRow.removeChild(bodyRow.lastElementChild);
4761      }
4762    }
4763  },
4764  updateTableCellStyles: function (row, cell, context, options) {
4765    var sectionMatrix = row && row.parentElement ? this.getCellMatrix(row.parentElement) : [];
4766    var resizePosition = row && row.parentElement ? this.getCellPosition(row.parentElement, cell, sectionMatrix) : { columnIndex: context.columnIndex };
4767    var resizeColumnIndex = resizePosition.columnIndex;
4768
4769    for (var rowIndex = 0; rowIndex < sectionMatrix.length; rowIndex++) {
4770      var syncCell = sectionMatrix[rowIndex][resizeColumnIndex];
4771      if (syncCell && options.columnWidth) {
4772        syncCell.style.width = options.columnWidth;
4773      } else if (syncCell) {
4774        syncCell.style.removeProperty('width');
4775      }
4776    }
4777
4778    options.cellBackground ? cell.style.backgroundColor = options.cellBackground : cell.style.removeProperty('background-color');
4779    options.cellPadding ? cell.style.padding = options.cellPadding : cell.style.removeProperty('padding');
4780    options.cellTextAlign ? cell.style.textAlign = options.cellTextAlign : cell.style.removeProperty('text-align');
4781    options.cellVerticalAlign ? cell.style.verticalAlign = options.cellVerticalAlign : cell.style.removeProperty('vertical-align');
4782
4783    if (options.cellBorder) {
4784      cell.style.border = options.cellBorder;
4785    } else {
4786      cell.style.borderTop = options.borderTop ? options.borderValue : 'none';
4787      cell.style.borderRight = options.borderRight ? options.borderValue : 'none';
4788      cell.style.borderBottom = options.borderBottom ? options.borderValue : 'none';
4789      cell.style.borderLeft = options.borderLeft ? options.borderValue : 'none';
4790    }
4791  },
4792  addTableRow: function (context, command) {
4793    var row = context.row;
4794    var newRow = this.createTableRow(context.ref, this.getTableColumnCount(context.table), context.inHeader);
4795    row.parentNode.insertBefore(newRow, command === 'addRowBefore' ? row : row.nextSibling);
4796  },
4797  deleteTableRow: function (context) {
4798    var table = context.table;
4799    if (context.row) {
4800      context.row.remove();
4801    }
4802    if (table.tHead && !table.tHead.rows.length) {
4803      table.deleteTHead();
4804    }
4805    if (table.tBodies.length && !table.tBodies[0].rows.length) {
4806      table.tBodies[0].appendChild(this.createTableRow(context.ref, this.getTableColumnCount(table) || 1, false));
4807    }
4808  },
4809  addTableColumn: function (context, command) {
4810    var self = this;
4811    var ref = context.ref;
4812    var table = context.table;
4813    var columnIndex = Math.max(context.columnIndex, 0);
4814    var insertIndex = command === 'addColumnBefore' ? columnIndex : columnIndex + 1;
4815    Array.prototype.forEach.call(table.rows, function (tableRow) {
4816      var header = table.tHead && table.tHead.contains(tableRow);
4817      var column = document.createElement(header ? 'th' : 'td');
4818      column.innerHTML = header ? self.formatTableColumnHeader(ref, insertIndex) : '&nbsp;';
4819      tableRow.insertBefore(column, tableRow.children[insertIndex] || null);
4820    });
4821  },
4822  deleteTableColumn: function (context) {
4823    var index = context.columnIndex;
4824    if (index >= 0) {
4825      Array.prototype.forEach.call(context.table.rows, function (tableRow) {
4826        if (tableRow.children.length > 1 && tableRow.children[index]) {
4827          tableRow.removeChild(tableRow.children[index]);
4828        }
4829      });
4830    }
4831  },
4832  mergeTableCellRight: function (ref, context) {
4833    var row = context.row;
4834    var cell = context.cell;
4835    var sectionRight = row && row.parentElement ? row.parentElement : null;
4836    var boundsRight = this.getSelectedCellBounds(ref, sectionRight);
4837    if (boundsRight && boundsRight.selectedCells.length > 1) {
4838      var anchorCellRight = (boundsRight.matrix[boundsRight.minRow] || [])[boundsRight.minColumn];
4839      if (!anchorCellRight) {
4840        return false;
4841      }
4842      anchorCellRight.setAttribute('colspan', Math.max(boundsRight.maxColumn - boundsRight.minColumn + 1, 1));
4843      anchorCellRight.setAttribute('rowspan', Math.max(boundsRight.maxRow - boundsRight.minRow + 1, 1));
4844      for (var mergeRow = boundsRight.minRow; mergeRow <= boundsRight.maxRow; mergeRow++) {
4845        for (var mergeCol = boundsRight.minColumn; mergeCol <= boundsRight.maxColumn; mergeCol++) {
4846          var mergedCell = (boundsRight.matrix[mergeRow] || [])[mergeCol];
4847          if (mergedCell && mergedCell !== anchorCellRight && mergedCell.parentElement) {
4848            mergedCell.remove();
4849          }
4850        }
4851      }
4852      return true;
4853    }
4854    if (cell && row && row.parentElement) {
4855      var rightMatrix = this.getCellMatrix(row.parentElement);
4856      var rightPosition = this.getCellPosition(row.parentElement, cell, rightMatrix);
4857      var currentRightSpan = Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1);
4858      var nextCell = (rightMatrix[rightPosition.rowIndex] || [])[rightPosition.columnIndex + currentRightSpan];
4859      if (!nextCell || nextCell === cell) {
4860        return true;
4861      }
4862      cell.setAttribute('colspan', currentRightSpan + Math.max(parseInt(nextCell.getAttribute('colspan') || 1, 10) || 1, 1));
4863      if (!cell.innerHTML || cell.innerHTML === '&nbsp;') {
4864        cell.innerHTML = nextCell.innerHTML;
4865      } else if (nextCell.innerHTML && nextCell.innerHTML !== '&nbsp;') {
4866        cell.innerHTML += ' ' + nextCell.innerHTML;
4867      }
4868      nextCell.remove();
4869    }
4870    return true;
4871  },
4872  mergeTableCellDown: function (context) {
4873    var cell = context.cell;
4874    var row = context.row;
4875    if (cell && row && row.parentElement) {
4876      var downMatrix = this.getCellMatrix(row.parentElement);
4877      var downPosition = this.getCellPosition(row.parentElement, cell, downMatrix);
4878      var currentRowSpan = Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1);
4879      var targetCell = (downMatrix[downPosition.rowIndex + currentRowSpan] || [])[downPosition.columnIndex];
4880      if (targetCell) {
4881        cell.setAttribute('rowspan', currentRowSpan + Math.max(parseInt(targetCell.getAttribute('rowspan') || 1, 10) || 1, 1));
4882        if (!cell.innerHTML || cell.innerHTML === '&nbsp;') {
4883          cell.innerHTML = targetCell.innerHTML;
4884        } else if (targetCell.innerHTML && targetCell.innerHTML !== '&nbsp;') {
4885          cell.innerHTML += '<br>' + targetCell.innerHTML;
4886        }
4887        targetCell.remove();
4888      }
4889    }
4890  },
4891  splitTableCell: function (context) {
4892    var cell = context.cell;
4893    var row = context.row;
4894    if (!cell) {
4895      return;
4896    }
4897    var splitColSpan = Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1);
4898    var splitRowSpan = Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1);
4899    var splitPosition = row && row.parentElement ? this.getCellPosition(row.parentElement, cell) : { rowIndex: row ? row.sectionRowIndex : 0, columnIndex: context.columnIndex };
4900    if (splitColSpan > 1) {
4901      cell.removeAttribute('colspan');
4902      for (var c = 1; c < splitColSpan; c++) {
4903        var splitCell = document.createElement(cell.tagName.toLowerCase());
4904        splitCell.innerHTML = '&nbsp;';
4905        cell.parentElement.insertBefore(splitCell, cell.parentElement.cells[(Array.prototype.indexOf.call(cell.parentElement.cells, cell) + c)] || null);
4906      }
4907    }
4908    if (splitRowSpan > 1 && row && row.parentElement) {
4909      cell.removeAttribute('rowspan');
4910      for (var sr = 1; sr < splitRowSpan; sr++) {
4911        var splitRow = row.parentElement.rows[row.sectionRowIndex + sr];
4912        if (splitRow) {
4913          var splitMatrix = this.getCellMatrix(row.parentElement);
4914          var splitDownCell = document.createElement(cell.tagName.toLowerCase());
4915          splitDownCell.innerHTML = '&nbsp;';
4916          var splitReference = null;
4917          for (var rc = splitPosition.columnIndex; rc < (splitMatrix[splitPosition.rowIndex + sr] || []).length; rc++) {
4918            if (splitMatrix[splitPosition.rowIndex + sr][rc] && splitMatrix[splitPosition.rowIndex + sr][rc] !== cell) {
4919              splitReference = splitMatrix[splitPosition.rowIndex + sr][rc];
4920              break;
4921            }
4922          }
4923          splitRow.insertBefore(splitDownCell, splitReference);
4924        }
4925      }
4926    }
4927  },
4928  execTableCommand: function (ref, name, value) {
4929    if (document.activeElement != ref && ref) {
4930      ref.focus();
4931    }
4932
4933    this.captureTableColumnHeaderFormat(ref, value);
4934
4935    try {
4936      if (name === 'insertTable') {
4937        return this.insertTable(ref, value);
4938      }
4939
4940      var context = (ref && ref.contextMenuTarget)
4941        ? this.getTableContextFromTarget(ref, ref.contextMenuTarget)
4942        : this.getTableContext(ref);
4943
4944      if (!context.table) {
4945        return this.createCommandResult(ref, false, 'table.action.requiresTable');
4946      }
4947
4948      switch (name) {
4949        case 'copyCells': return this.copyTableCells(ref);
4950        case 'pasteCells': return this.pasteTableCells(ref, context);
4951        case 'updateTable': this.updateTable(context, value); break;
4952        case 'addRowBefore':
4953        case 'addRowAfter': this.addTableRow(context, name); break;
4954        case 'deleteRow': this.deleteTableRow(context); break;
4955        case 'addColumnBefore':
4956        case 'addColumnAfter': this.addTableColumn(context, name); break;
4957        case 'deleteColumn': this.deleteTableColumn(context); break;
4958        case 'mergeCellRight':
4959          if (!this.mergeTableCellRight(ref, context)) {
4960            return this.createCommandResult(ref, false, 'table.merge.invalidSelection');
4961          }
4962          break;
4963        case 'mergeCellDown': this.mergeTableCellDown(context); break;
4964        case 'splitCell': this.splitTableCell(context); break;
4965        case 'deleteTable': this.deleteTable(context); break;
4966      }
4967
4968      return this.createCommandResult(ref, true);
4969    } finally {
4970      if (ref) {
4971        ref.contextMenuTarget = null;
4972      }
4973    }
4974  },
4975  insertTable: function (ref, value) {
4976    this.captureTableColumnHeaderFormat(ref, value);
4977    document.execCommand('insertHTML', false, this.createTableHTML(ref, value));
4978    return this.createCommandResult(ref, true);
4979  },
4980  captureTableColumnHeaderFormat: function (ref, value) {
4981    if (ref && value && typeof value.defaultColumnHeader === 'string' && value.defaultColumnHeader) {
4982      ref.tableColumnHeaderFormat = value.defaultColumnHeader;
4983    }
4984  },
4985  copyTableCells: function (ref) {
4986    return this.copySelectedTableCells(ref)
4987      ? this.createCommandResult(ref, true)
4988      : this.createCommandResult(ref, false, 'table.copy.invalidSelection');
4989  },
4990  pasteTableCells: function (ref, context) {
4991    return this.pasteSelectedTableCells(ref, context.cell)
4992      ? this.createCommandResult(ref, true)
4993      : this.createCommandResult(ref, false, 'table.paste.blocked');
4994  },
4995  deleteTable: function (context) {
4996    context.table.remove();
4997  },
4998  getEditorHtml: function (ref) {
4999    var selected = Array.from(ref.querySelectorAll('img.rz-state-selected'));
5000
5001    for (var img of selected) {
5002      img.classList.remove('rz-state-selected');
5003
5004      if (!img.getAttribute('class')) {
5005        img.removeAttribute('class');
5006      }
5007    }
5008
5009    var html = ref.innerHTML;
5010
5011    for (var img of selected) {
5012      img.classList.add('rz-state-selected');
5013    }
5014
5015    return html;
5016  },
5017  queryCommands: function (ref) {
5018    return {
5019      html: ref != null ? this.getEditorHtml(ref) : null,
5020      fontName: document.queryCommandValue('fontName'),
5021      fontSize: document.queryCommandValue('fontSize'),
5022      formatBlock: document.queryCommandValue('formatBlock'),
5023      bold: document.queryCommandState('bold'),
5024      underline: document.queryCommandState('underline'),
5025      justifyRight: document.queryCommandState('justifyRight'),
5026      justifyLeft: document.queryCommandState('justifyLeft'),
5027      justifyCenter: document.queryCommandState('justifyCenter'),
5028      justifyFull: document.queryCommandState('justifyFull'),
5029      italic: document.queryCommandState('italic'),
5030      strikeThrough: document.queryCommandState('strikeThrough'),
5031      superscript: document.queryCommandState('superscript'),
5032      subscript: document.queryCommandState('subscript'),
5033      unlink: document.queryCommandEnabled('unlink'),
5034      undo: document.queryCommandEnabled('undo'),
5035      redo: document.queryCommandEnabled('redo')
5036    };
5037  },
5038  mediaQueries: {},
5039  mediaQuery: function(query, instance) {
5040    if (instance) {
5041      function callback(event) {
5042          try { suppressDisposed(instance.invokeMethodAsync('OnChange', event.matches)); } catch { }
5043      };
5044      var query = matchMedia(query);
5045      this.mediaQueries[instance._id] = function() {
5046          query.removeListener(callback);
5047      }
5048      query.addListener(callback);
5049      return query.matches;
5050    } else {
5051        instance = query;
5052        if (this.mediaQueries[instance._id]) {
5053            this.mediaQueries[instance._id]();
5054            delete this.mediaQueries[instance._id];
5055        }
5056    }
5057  },
5058  createEditor: function (ref, uploadUrl, paste, instance, shortcuts) {
5059    function trackDrag(move, end, key) {
5060      document.addEventListener('mousemove', move);
5061      document.addEventListener('mouseup', end);
5062
5063      if (key) {
5064        document.addEventListener('keydown', key, true);
5065      }
5066
5067      return function () {
5068        document.removeEventListener('mousemove', move);
5069        document.removeEventListener('mouseup', end);
5070
5071        if (key) {
5072          document.removeEventListener('keydown', key, true);
5073        }
5074      };
5075    }
5076
5077    var imageHandleDirections = {
5078      nw: [-1, -1], n: [0, -1], ne: [1, -1],
5079      w: [-1, 0], e: [1, 0],
5080      sw: [-1, 1], s: [0, 1], se: [1, 1]
5081    };
5082    var minImageSize = 24;
5083
5084    ref.getSelectedImage = function () {
5085      return ref.querySelector('img.rz-state-selected');
5086    };
5087
5088    ref.getEditorContainer = function () {
5089      return ref.closest('.rz-html-editor');
5090    };
5091
5092    ref.removeImageHandles = function () {
5093      if (!ref.imageHandles) {
5094        return;
5095      }
5096
5097      window.removeEventListener('resize', ref.positionImageHandles);
5098      ref.imageHandlesClip.remove();
5099      ref.imageHandlesClip = null;
5100      ref.imageHandles = null;
5101      ref.imageSizeLabel = null;
5102      ref.imageSizeLabelSpace = 0;
5103    };
5104
5105    ref.positionImageHandles = function () {
5106      if (!ref.imageHandles) {
5107        return;
5108      }
5109
5110      var img = ref.getSelectedImage();
5111      var container = ref.getEditorContainer();
5112
5113      if (!img || !container || ref.hidden || !ref.isContentEditable) {
5114        ref.removeImageHandles();
5115        return;
5116      }
5117
5118      var containerRect = container.getBoundingClientRect();
5119      var contentRect = ref.getBoundingClientRect();
5120      var imgRect = img.getBoundingClientRect();
5121      var clipLeft = contentRect.left - containerRect.left - container.clientLeft;
5122      var clipTop = contentRect.top - containerRect.top - container.clientTop;
5123
5124      ref.imageHandlesClip.style.left = clipLeft + 'px';
5125      ref.imageHandlesClip.style.top = clipTop + 'px';
5126      ref.imageHandlesClip.style.width = contentRect.width + 'px';
5127      ref.imageHandlesClip.style.height = contentRect.height + 'px';
5128
5129      ref.imageHandles.style.left = (imgRect.left - contentRect.left) + 'px';
5130      ref.imageHandles.style.top = (imgRect.top - contentRect.top) + 'px';
5131      ref.imageHandles.style.width = imgRect.width + 'px';
5132      ref.imageHandles.style.height = imgRect.height + 'px';
5133
5134      var label = ref.imageSizeLabel;
5135
5136      if (label && !label.hidden) {
5137        label.textContent = Math.round(imgRect.width) + ' × ' + Math.round(imgRect.height);
5138        label.classList.toggle('rz-inside', imgRect.bottom + ref.imageSizeLabelSpace > contentRect.bottom);
5139      }
5140    };
5141
5142    ref.createImageHandles = function () {
5143      ref.removeImageHandles();
5144
5145      var img = ref.getSelectedImage();
5146      var container = ref.getEditorContainer();
5147
5148      if (!img || !container || !ref.isContentEditable) {
5149        return;
5150      }
5151
5152      var clip = document.createElement('div');
5153      clip.className = 'rz-html-editor-image-clip';
5154      clip.setAttribute('contenteditable', 'false');
5155
5156      var handles = document.createElement('div');
5157      handles.className = 'rz-html-editor-image-handles';
5158
5159      Object.keys(imageHandleDirections).forEach(function (position) {
5160        var handle = document.createElement('div');
5161        handle.className = 'rz-html-editor-image-handle rz-' + position;
5162        handle.dataset.position = position;
5163        handle.addEventListener('mousedown', ref.imageResizeStartListener);
5164        handles.appendChild(handle);
5165      });
5166
5167      ref.imageSizeLabel = document.createElement('div');
5168      ref.imageSizeLabel.className = 'rz-html-editor-image-size';
5169      ref.imageSizeLabel.hidden = true;
5170      handles.appendChild(ref.imageSizeLabel);
5171
5172      clip.appendChild(handles);
5173      container.appendChild(clip);
5174      ref.imageHandlesClip = clip;
5175      ref.imageHandles = handles;
5176
5177      window.addEventListener('resize', ref.positionImageHandles);
5178      ref.positionImageHandles();
5179    };
5180
5181    ref.showImageSizeLabel = function (visible) {
5182      var label = ref.imageSizeLabel;
5183
5184      if (!label) {
5185        return;
5186      }
5187
5188      label.hidden = !visible;
5189
5190      if (visible) {
5191        label.textContent = '0 × 0';
5192        ref.imageSizeLabelSpace = label.offsetHeight + parseFloat(getComputedStyle(label).marginTop || 0);
5193      }
5194    };
5195
5196    ref.imageResizeStartListener = function (e) {
5197      var img = ref.getSelectedImage();
5198
5199      if (!img || ref.imageResize) {
5200        return;
5201      }
5202
5203      e.preventDefault();
5204      e.stopPropagation();
5205
5206      ref.imageResize = {
5207        img: img,
5208        direction: imageHandleDirections[e.currentTarget.dataset.position],
5209        startX: e.clientX,
5210        startY: e.clientY,
5211        startWidth: img.offsetWidth,
5212        startHeight: img.offsetHeight,
5213        startAttributes: { width: img.getAttribute('width'), height: img.getAttribute('height') },
5214        startStyles: { width: img.style.width, height: img.style.height },
5215        ratio: img.offsetHeight ? img.offsetWidth / img.offsetHeight : 1
5216      };
5217
5218      img.style.width = '';
5219      img.style.height = '';
5220      img.setAttribute('width', ref.imageResize.startWidth);
5221      img.setAttribute('height', ref.imageResize.startHeight);
5222
5223      ref.releaseImageDrag = trackDrag(
5224        ref.documentImageResizeMoveListener,
5225        ref.documentImageResizeEndListener,
5226        ref.documentImageResizeKeyListener);
5227      ref.showImageSizeLabel(true);
5228      ref.positionImageHandles();
5229    };
5230
5231    ref.restoreImageSize = function (state) {
5232      Object.keys(state.startAttributes).forEach(function (name) {
5233        var value = state.startAttributes[name];
5234
5235        if (value !== null) {
5236          state.img.setAttribute(name, value);
5237        } else {
5238          state.img.removeAttribute(name);
5239        }
5240
5241        state.img.style[name] = state.startStyles[name];
5242      });
5243    };
5244
5245    ref.documentImageResizeKeyListener = function (e) {
5246      if (e.key !== 'Escape' || !ref.imageResize) {
5247        return;
5248      }
5249
5250      e.preventDefault();
5251      e.stopPropagation();
5252      ref.restoreImageSize(ref.imageResize);
5253      ref.stopImageDrag();
5254      ref.positionImageHandles();
5255    };
5256
5257    ref.documentImageResizeMoveListener = function (e) {
5258      var state = ref.imageResize;
5259
5260      if (!state || !ref.isConnected) {
5261        return;
5262      }
5263
5264      e.preventDefault();
5265
5266      var horizontal = state.direction[0];
5267      var vertical = state.direction[1];
5268      var width = state.startWidth;
5269      var height = state.startHeight;
5270
5271      if (horizontal) {
5272        width = Math.max(Math.round(state.startWidth + horizontal * (e.clientX - state.startX)), minImageSize);
5273      }
5274
5275      if (vertical && (!horizontal || e.shiftKey)) {
5276        height = Math.max(Math.round(state.startHeight + vertical * (e.clientY - state.startY)), minImageSize);
5277      } else if (horizontal && vertical) {
5278        height = Math.max(Math.round(width / state.ratio), minImageSize);
5279      }
5280
5281      state.img.setAttribute('width', width);
5282      state.img.setAttribute('height', height);
5283
5284      ref.positionImageHandles();
5285    };
5286
5287    ref.documentImageResizeEndListener = function () {
5288      var state = ref.imageResize;
5289
5290      if (!state) {
5291        return;
5292      }
5293
5294      var img = state.img;
5295      var width = img.getAttribute('width');
5296      var height = img.getAttribute('height');
5297
5298      var index = Array.prototype.indexOf.call(ref.querySelectorAll('img'), img);
5299
5300      ref.stopImageDrag();
5301      ref.restoreImageSize(state);
5302
5303      if (width === String(state.startWidth) && height === String(state.startHeight)) {
5304        ref.positionImageHandles();
5305        return;
5306      }
5307
5308      ref.deselectImage(img);
5309
5310      var target = img;
5311
5312      while (target.parentElement && target.parentElement !== ref && getComputedStyle(target.parentElement).display === 'inline') {
5313        target = target.parentElement;
5314      }
5315
5316      var replacement = target.cloneNode(true);
5317      var replacementImg = target === img
5318        ? replacement
5319        : replacement.querySelectorAll('img')[Array.prototype.indexOf.call(target.querySelectorAll('img'), img)];
5320
5321      replacementImg.setAttribute('width', width);
5322      replacementImg.setAttribute('height', height);
5323      replacementImg.style.width = '';
5324      replacementImg.style.height = '';
5325
5326      if (!replacementImg.style.length) {
5327        replacementImg.removeAttribute('style');
5328      }
5329
5330      var range = document.createRange();
5331      range.selectNode(target);
5332      var selection = getSelection();
5333      selection.removeAllRanges();
5334      selection.addRange(range);
5335      ref.focus();
5336      document.execCommand('insertHTML', false, replacement.outerHTML);
5337
5338      ref.selectImage(ref.querySelectorAll('img')[index]);
5339    };
5340
5341    ref.deselectImage = function (img) {
5342      img.classList.remove('rz-state-selected');
5343
5344      if (!img.getAttribute('class')) {
5345        img.removeAttribute('class');
5346      }
5347
5348      ref.removeImageHandles();
5349    };
5350
5351    ref.selectImage = function (img) {
5352      if (!img) {
5353        ref.removeImageHandles();
5354        return;
5355      }
5356
5357      img.classList.add('rz-state-selected');
5358
5359      var range = document.createRange();
5360      range.selectNode(img);
5361      var selection = getSelection();
5362      selection.removeAllRanges();
5363      selection.addRange(range);
5364
5365      ref.createImageHandles();
5366    };
5367
5368    ref.imageHandlesObserver = new MutationObserver(ref.positionImageHandles);
5369
5370    ref.stopImageDrag = function () {
5371      ref.imageResize = null;
5372      ref.showImageSizeLabel(false);
5373
5374      if (ref.releaseImageDrag) {
5375        ref.releaseImageDrag();
5376        ref.releaseImageDrag = null;
5377      }
5378    };
5379
5380    ref.inputListener = function () {
5381      ref.positionImageHandles();
5382      try { suppressDisposed(instance.invokeMethodAsync('OnChange', Radzen.getEditorHtml(ref))); } catch { }
5383    };
5384    ref.keydownListener = function (e) {
5385      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'c') {
5386        var currentCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5387        if (currentCell && ref.contains(currentCell)) {
5388          if (!currentCell.classList.contains('rz-html-editor-cell-selected')) {
5389            Radzen.clearSelectedTableCells(ref);
5390            currentCell.classList.add('rz-html-editor-cell-selected');
5391          }
5392          e.preventDefault();
5393          Radzen.copySelectedTableCells(ref);
5394          return;
5395        }
5396      }
5397
5398      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'v') {
5399        var pasteCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5400        if (pasteCell && ref.contains(pasteCell) && ref.copiedCells && ref.copiedCells.matrix) {
5401          e.preventDefault();
5402          Radzen.pasteSelectedTableCells(ref, pasteCell);
5403          try { instance.invokeMethodAsync('OnChange', ref.innerHTML); } catch { }
5404          return;
5405        }
5406      }
5407
5408      if (e.key === 'Tab') {
5409        var activeCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5410        if (activeCell && ref.contains(activeCell)) {
5411          e.preventDefault();
5412          var activeRow = activeCell.parentElement;
5413          var section = activeRow ? activeRow.parentElement : null;
5414          var matrix = Radzen.getCellMatrix(section);
5415          var activePosition = Radzen.getCellPosition(section, activeCell, matrix);
5416          var rowIndex = activePosition.rowIndex;
5417          var columnIndex = activePosition.columnIndex;
5418          var colSpan = Math.max(parseInt(activeCell.getAttribute('colspan') || 1, 10) || 1, 1);
5419          var rowSpan = Math.max(parseInt(activeCell.getAttribute('rowspan') || 1, 10) || 1, 1);
5420          var nextColumnIndex = e.shiftKey ? columnIndex - 1 : columnIndex + colSpan;
5421          var nextRowIndex = rowIndex;
5422
5423          if (nextColumnIndex < 0) {
5424            nextRowIndex = Math.max(rowIndex - 1, 0);
5425            nextColumnIndex = Math.max((matrix[nextRowIndex] || []).length - 1, 0);
5426          }
5427
5428          while (matrix[nextRowIndex] && !matrix[nextRowIndex][nextColumnIndex] && nextColumnIndex >= 0) {
5429            nextColumnIndex += e.shiftKey ? -1 : 1;
5430          }
5431
5432          if ((!matrix[nextRowIndex] || !matrix[nextRowIndex][nextColumnIndex]) && !e.shiftKey) {
5433            nextRowIndex = rowIndex + rowSpan;
5434            nextColumnIndex = 0;
5435          }
5436
5437          var nextCell = (matrix[nextRowIndex] || [])[nextColumnIndex];
5438          if (!nextCell && !e.shiftKey && section && activeRow && activeRow.parentElement && activeRow.parentElement.tagName === 'TBODY') {
5439            var table = activeCell.closest('table');
5440            if (table) {
5441              Radzen.execTableCommand(ref, 'addRowAfter');
5442              matrix = Radzen.getCellMatrix(section);
5443              nextRowIndex = Math.min(rowIndex + rowSpan, matrix.length - 1);
5444              nextCell = (matrix[nextRowIndex] || [])[0];
5445            }
5446          }
5447
5448          if (nextCell) {
5449            var range = document.createRange();
5450            range.selectNodeContents(nextCell);
5451            range.collapse(true);
5452            var selection = getSelection();
5453            selection.removeAllRanges();
5454            selection.addRange(range);
5455            ref.focus();
5456          }
5457          return;
5458        }
5459      }
5460
5461      var key = '';
5462      if (e.ctrlKey || e.metaKey) {
5463        key += 'Ctrl+';
5464      }
5465      if (e.altKey) {
5466        key += 'Alt+';
5467      }
5468      if (e.shiftKey) {
5469        key += 'Shift+';
5470      }
5471      key += e.code.replace('Key', '').replace('Digit', '').replace('Numpad', '');
5472
5473      if (shortcuts.includes(key)) {
5474        e.preventDefault();
5475        try { suppressDisposed(instance.invokeMethodAsync('ExecuteShortcutAsync', key)); } catch { }
5476      }
5477    };
5478
5479    ref.clickListener = function (e) {
5480      if (e.target) {
5481        if (e.target.matches('a,button')) {
5482          e.preventDefault();
5483        }
5484
5485        for (var img of ref.querySelectorAll('img.rz-state-selected')) {
5486          ref.deselectImage(img);
5487        }
5488
5489        if (e.target.matches('img')) {
5490          ref.selectImage(e.target);
5491        } else {
5492          var clickedCell = e.target.closest && e.target.closest('td,th');
5493          if (clickedCell && ref.contains(clickedCell)) {
5494            if (e.shiftKey && ref.lastSelectedTableCell) {
5495              Radzen.selectTableCellRange(ref, ref.lastSelectedTableCell, clickedCell);
5496            } else if (e.ctrlKey || e.metaKey) {
5497              clickedCell.classList.toggle('rz-html-editor-cell-selected');
5498            } else {
5499              Radzen.clearSelectedTableCells(ref);
5500              clickedCell.classList.add('rz-html-editor-cell-selected');
5501            }
5502            ref.lastSelectedTableCell = clickedCell;
5503          } else {
5504            Radzen.clearSelectedTableCells(ref);
5505            ref.lastSelectedTableCell = null;
5506          }
5507        }
5508      }
5509    }
5510
5511    ref.contextMenuListener = function (e) {
5512      var cell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5513      if (cell && ref.contains(cell)) {
5514        if (!cell.classList.contains('rz-html-editor-cell-selected')) {
5515          Radzen.clearSelectedTableCells(ref);
5516          cell.classList.add('rz-html-editor-cell-selected');
5517        }
5518        ref.lastSelectedTableCell = cell;
5519        ref.contextMenuTarget = cell;
5520        e.preventDefault();
5521        try { instance.invokeMethodAsync('OnContextMenu', e.clientX, e.clientY); } catch { }
5522      }
5523    }
5524
5525    ref.mousemoveListener = function (e) {
5526      var hoveredCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5527      if (!hoveredCell || !ref.contains(hoveredCell)) {
5528        ref.style.cursor = '';
5529        return;
5530      }
5531
5532      var rect = hoveredCell.getBoundingClientRect();
5533      var nearRightEdge = Math.abs(rect.right - e.clientX) < 6;
5534      if (nearRightEdge) {
5535        ref.style.cursor = 'col-resize';
5536        ref.resizeTargetCell = hoveredCell;
5537      } else if (!ref.isResizingColumn) {
5538        ref.style.cursor = '';
5539        ref.resizeTargetCell = null;
5540      }
5541    };
5542
5543    ref.mousedownResizeListener = function (e) {
5544      if (ref.resizeTargetCell && ref.style.cursor === 'col-resize') {
5545        e.preventDefault();
5546        ref.isResizingColumn = true;
5547        ref.resizeStartX = e.clientX;
5548        ref.resizeStartWidth = ref.resizeTargetCell.getBoundingClientRect().width;
5549        ref.releaseColumnDrag = trackDrag(ref.documentMouseMoveResizeListener, ref.mouseupResizeListener);
5550      }
5551    };
5552
5553    ref.mouseupResizeListener = function () {
5554      if (ref.isResizingColumn) {
5555        ref.isResizingColumn = false;
5556        ref.style.cursor = '';
5557        ref.releaseColumnDrag();
5558        ref.releaseColumnDrag = null;
5559        ref.inputListener();
5560      }
5561    };
5562
5563    ref.documentMouseMoveResizeListener = function (e) {
5564      if (ref.isResizingColumn && ref.resizeTargetCell && ref.isConnected) {
5565        var width = Math.max(ref.resizeStartWidth + (e.clientX - ref.resizeStartX), 24);
5566        var resizeSection = ref.resizeTargetCell.parentElement ? ref.resizeTargetCell.parentElement.parentElement : null;
5567        var resizeMatrix = Radzen.getCellMatrix(resizeSection);
5568        var resizePosition = Radzen.getCellPosition(resizeSection, ref.resizeTargetCell, resizeMatrix);
5569        var resizeColumnIndex = resizePosition.columnIndex;
5570        for (var resizeRowIndex = 0; resizeRowIndex < resizeMatrix.length; resizeRowIndex++) {
5571          var resizeCell = resizeMatrix[resizeRowIndex][resizeColumnIndex];
5572          if (resizeCell) {
5573            resizeCell.style.width = width + 'px';
5574          }
5575        }
5576      }
5577    };
5578
5579    ref.selectionChangeListener = function () {
5580      if (document.activeElement == ref) {
5581        var selectedImage = ref.getSelectedImage();
5582
5583        if (selectedImage && !ref.imageResize && !getSelection().containsNode(selectedImage)) {
5584          ref.deselectImage(selectedImage);
5585        }
5586
5587        try { suppressDisposed(instance.invokeMethodAsync('OnSelectionChange')); } catch { }
5588      }
5589    };
5590    ref.handleInsert = function (e, transfer, hasDelegate) {
5591      var hasFiles = transfer.files.length > 0;
5592      var hasHtml = transfer.types.indexOf('text/html') > -1;
5593      var hasText = transfer.types.indexOf('text/plain') > -1;
5594
5595      if (hasFiles && !hasHtml && !hasText) {
5596        for (const file of transfer.files) {
5597          ref.handleFileInsert(e, file, hasDelegate);
5598        }
5599      }
5600      else if (hasDelegate) {
5601        ref.handleTextInsert(e, transfer);
5602      }
5603    };
5604    ref.handleFileInsert = function (event, file, hasDelegate) {
5605      event.preventDefault();
5606
5607      if (uploadUrl) {
5608        var xhr = new XMLHttpRequest();
5609        var data = new FormData();
5610        data.append("file", file);
5611        xhr.onreadystatechange = function (e) {
5612          if (xhr.readyState === XMLHttpRequest.DONE) {
5613            var status = xhr.status;
5614            if (status === 0 || (status >= 200 && status < 400)) {
5615              var result = JSON.parse(xhr.responseText);
5616              var html = '<img src="' + result.url + '">';
5617              if (hasDelegate) {
5618                try { instance.invokeMethodAsync('OnPaste', html)
5619                  .then(function (html) {
5620                    document.execCommand("insertHTML", false, html);
5621                  }); } catch { }
5622              } else {
5623                document.execCommand("insertHTML", false, '<img src="' + result.url + '">');
5624              }
5625              try { suppressDisposed(instance.invokeMethodAsync('OnUploadComplete', xhr.responseText)); } catch { }
5626            } else {
5627              try { suppressDisposed(instance.invokeMethodAsync('OnError', xhr.responseText)); } catch { }
5628            }
5629          }
5630        }
5631        try { instance.invokeMethodAsync('GetHeaders').then(function (headers) {
5632          xhr.open('POST', uploadUrl, true);
5633          for (var name in headers) {
5634            xhr.setRequestHeader(name, headers[name]);
5635          }
5636          xhr.send(data);
5637        }); } catch { }
5638      } else {
5639        var reader = new FileReader();
5640        reader.onload = function (e) {
5641          var html = '<img src="' + e.target.result + '">';
5642
5643          if (hasDelegate) {
5644            try { instance.invokeMethodAsync('OnPaste', html)
5645              .then(function (html) {
5646                document.execCommand("insertHTML", false, html);
5647              }); } catch { }
5648            } else {
5649              document.execCommand("insertHTML", false, html);
5650            }
5651        };
5652        reader.readAsDataURL(file);
5653      }
5654    }
5655    ref.handleTextInsert = function (e, transfer) {
5656      e.preventDefault();
5657
5658      var data = transfer.getData('text/html') || transfer.getData('text/plain');
5659
5660      const startMarker = "<!--StartFragment-->";
5661      const endMarker = "<!--EndFragment-->";
5662
5663      const startIndex = data.indexOf(startMarker);
5664      const endIndex = data.indexOf(endMarker);
5665
5666      // check if the pasted data contains fragment markers
5667      if (startIndex != -1 || endIndex != -1 || endIndex > startIndex) {
5668        // only paste the fragment
5669        data = data.substring(startIndex + startMarker.length, endIndex).trim();
5670      }
5671
5672      try { instance.invokeMethodAsync('OnPaste', data)
5673        .then(ref.focus())
5674        .then(function (html) {
5675          document.execCommand("insertHTML", false, html);
5676        }); } catch { }
5677    }
5678    ref.pasteListener = function (e) {
5679      ref.handleInsert(e, e.clipboardData, paste);
5680    };
5681    ref.dropListener = function (e) {
5682      ref.handleInsert(e, e.dataTransfer, paste);
5683    };
5684    ref.addEventListener('input', ref.inputListener);
5685    ref.addEventListener('paste', ref.pasteListener);
5686    ref.addEventListener('drop', ref.dropListener);
5687    ref.addEventListener('keydown', ref.keydownListener);
5688    ref.addEventListener('click', ref.clickListener);
5689    ref.addEventListener('contextmenu', ref.contextMenuListener);
5690    ref.addEventListener('mousemove', ref.mousemoveListener);
5691    ref.addEventListener('mousedown', ref.mousedownResizeListener);
5692    ref.addEventListener('scroll', ref.positionImageHandles);
5693    ref.imageHandlesObserver.observe(ref, { attributes: true, attributeFilter: ['hidden', 'contenteditable'], childList: true });
5694    document.addEventListener('selectionchange', ref.selectionChangeListener);
5695    document.execCommand('styleWithCSS', false, true);
5696    return {
5697      dispose: function () {
5698        if (ref) {
5699          ref.removeEventListener('input', ref.inputListener);
5700          ref.removeEventListener('paste', ref.pasteListener);
5701          ref.removeEventListener('drop', ref.dropListener);
5702          ref.removeEventListener('keydown', ref.keydownListener);
5703          ref.removeEventListener('click', ref.clickListener);
5704          ref.removeEventListener('contextmenu', ref.contextMenuListener);
5705          ref.removeEventListener('mousemove', ref.mousemoveListener);
5706          ref.removeEventListener('mousedown', ref.mousedownResizeListener);
5707          ref.removeEventListener('scroll', ref.positionImageHandles);
5708          ref.imageHandlesObserver.disconnect();
5709          ref.stopImageDrag();
5710          ref.removeImageHandles();
5711          ref.isResizingColumn = false;
5712
5713          if (ref.releaseColumnDrag) {
5714            ref.releaseColumnDrag();
5715            ref.releaseColumnDrag = null;
5716          }
5717          document.removeEventListener('selectionchange', ref.selectionChangeListener);
5718        }
5719      }
5720    };
5721  },
5722  saveSelection: function (ref) {
5723    if (!ref) return;
5724    var selection = getSelection();
5725    if (selection.rangeCount > 0) {
5726      var range = selection.getRangeAt(0);
5727      if (ref.contains(range.commonAncestorContainer)) {
5728        ref.range = range;
5729      }
5730    }
5731  },
5732  restoreSelection: function (ref) {
5733    if (!ref) return;
5734    var range = ref.range;
5735    if (range) {
5736      delete ref.range;
5737      if(ref) {
5738          ref.focus();
5739      }
5740      var selection = getSelection();
5741      selection.removeAllRanges();
5742      selection.addRange(range);
5743    }
5744  },
5745  selectionAttributes: function (selector, attributes, container) {
5746    var selection = getSelection();
5747    var range = selection.rangeCount > 0 && selection.getRangeAt(0);
5748    var parent = range && range.commonAncestorContainer;
5749    var img = container.querySelector('img.rz-state-selected');
5750    var inside = img && selector == 'img';
5751    while (parent) {
5752      if (parent == container) {
5753        inside = true;
5754        break;
5755      }
5756      parent = parent.parentNode;
5757    }
5758    if (!inside) {
5759      return {};
5760    }
5761    var target = selection.focusNode;
5762    var innerHTML;
5763
5764    if (img && selector == 'img') {
5765      target = img;
5766    } else if (target) {
5767      if (target.nodeType == 3) {
5768        target = target.parentElement;
5769      } else {
5770        if (img) {
5771          target = target.childNodes[range.startOffset]
5772        } else {
5773          target = target.childNodes[selection.focusOffset];
5774        }
5775        if (target) {
5776          innerHTML = target.outerHTML;
5777        }
5778      }
5779      if (target && target.matches && !target.matches(selector)) {
5780        target = target.closest(selector);
5781      }
5782    }
5783
5784    return attributes.reduce(function (result, name) {
5785      if (target) {
5786        result[name] = name == 'innerText' ? target[name] : target.getAttribute(name);
5787      }
5788      return result;
5789    }, { innerText: selection.toString(), innerHTML: innerHTML });
5790  },
5791  startDrag: function (ref, instance, handler) {
5792    if (!ref) {
5793        return { left: 0, top: 0, width: 0, height: 0 };
5794    }
5795    Radzen.endDrag(ref);
5796    ref.mouseMoveHandler = function (e) {
5797      try { suppressDisposed(instance.invokeMethodAsync(handler, { clientX: e.clientX, clientY: e.clientY })); } catch { }
5798    };
5799    ref.touchMoveHandler = function (e) {
5800      if (e.targetTouches[0] && ref.contains(e.targetTouches[0].target)) {
5801        try { suppressDisposed(instance.invokeMethodAsync(handler, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY })); } catch { }
5802      }
5803    };
5804    ref.mouseUpHandler = function (e) {
5805      Radzen.endDrag(ref);
5806    };
5807    ref.dragStartHandler = function (e) {
5808      e.preventDefault();
5809    };
5810    document.addEventListener('mousemove', ref.mouseMoveHandler);
5811    document.addEventListener('mouseup', ref.mouseUpHandler);
5812    document.addEventListener('touchmove', ref.touchMoveHandler, { passive: true, capture: true })
5813    document.addEventListener('touchend', ref.mouseUpHandler, { passive: true });
5814    document.addEventListener('dragstart', ref.dragStartHandler);
5815    document.addEventListener('dragend', ref.mouseUpHandler);
5816    return Radzen.clientRect(ref);
5817  },
5818  submit: function (form) {
5819    form.submit();
5820  },
5821  clientRect: function (arg) {
5822    var el = arg instanceof Element || arg instanceof HTMLDocument
5823        ? arg
5824        : document.getElementById(arg);
5825    var rect = el.getBoundingClientRect();
5826    return { left: rect.left, top: rect.top, width: rect.width, height: rect.height };
5827  },
5828  outerHTML: function (arg) {
5829    var el = arg instanceof Element || arg instanceof HTMLDocument
5830        ? arg
5831        : document.getElementById(arg);
5832    return el ? el.outerHTML : '';
5833  },
5834  endDrag: function (ref) {
5835    document.removeEventListener('mousemove', ref.mouseMoveHandler);
5836    document.removeEventListener('mouseup', ref.mouseUpHandler);
5837    document.removeEventListener('touchmove', ref.touchMoveHandler)
5838    document.removeEventListener('touchend', ref.mouseUpHandler);
5839    document.removeEventListener('dragstart', ref.dragStartHandler);
5840    document.removeEventListener('dragend', ref.mouseUpHandler);
5841  },
5842  capturePointer: function (el, pointerId) {
5843    try {
5844      if (el && el.setPointerCapture) {
5845        el.setPointerCapture(pointerId);
5846      }
5847    } catch (e) { /* ignore */ }
5848  },
5849  releasePointer: function (el, pointerId) {
5850    try {
5851      if (el && el.releasePointerCapture && (!el.hasPointerCapture || el.hasPointerCapture(pointerId))) {
5852        el.releasePointerCapture(pointerId);
5853      }
5854    } catch (e) { /* ignore */ }
5855  },
5856  startColumnReorder: function(id, gridId, gridRef) {
5857      if (Radzen[id + 'reorderCleanup']) {
5858          Radzen[id + 'reorderCleanup']();
5859      }
5860
5861      var grid = document.getElementById(gridId);
5862      var el = document.getElementById(id + '-drag');
5863      var cell = el.parentNode.parentNode;
5864      var visual = document.createElement("th");
5865      visual.className = cell.className + ' rz-column-draggable';
5866      visual.style = cell.style;
5867      visual.style.display = 'none';
5868      visual.style.position = 'absolute';
5869      visual.style.height = cell.offsetHeight + 'px';
5870      visual.style.width = cell.offsetWidth + 'px';
5871      visual.style.zIndex = 2000;
5872      visual.style.pointerEvents = 'none';
5873      visual.innerHTML = cell.firstChild.outerHTML;
5874      visual.id = id + 'visual';
5875      document.body.appendChild(visual);
5876
5877      var resizers = cell.parentNode.querySelectorAll('.rz-column-resizer');
5878      for (let i = 0; i < resizers.length; i++) {
5879          resizers[i].style.display = 'none';
5880      }
5881
5882      var lastTouchX = null;
5883      var lastTouchY = null;
5884      var scroller = grid.querySelector('.rz-data-grid-data');
5885      var autoScrollSpeed = 0;
5886      var autoScrollFrame = null;
5887
5888      var stopAutoScroll = function () {
5889          if (autoScrollFrame) {
5890              cancelAnimationFrame(autoScrollFrame);
5891          }
5892          autoScrollFrame = null;
5893          autoScrollSpeed = 0;
5894      };
5895
5896      var autoScrollStep = function () {
5897          autoScrollFrame = null;
5898          if (!autoScrollSpeed || !scroller) {
5899              return;
5900          }
5901          var before = scroller.scrollLeft;
5902          scroller.scrollLeft = before + autoScrollSpeed;
5903          if (Math.abs(scroller.scrollLeft - before) >= 0.5) {
5904              autoScrollFrame = requestAnimationFrame(autoScrollStep);
5905          }
5906      };
5907
5908      var updateAutoScroll = function (clientX, clientY) {
5909          if (!scroller || scroller.scrollWidth <= scroller.clientWidth) {
5910              return;
5911          }
5912          var gridRect = grid.getBoundingClientRect();
5913          var speed = 0;
5914          if (clientY >= gridRect.top && clientY <= gridRect.bottom) {
5915              var rect = scroller.getBoundingClientRect();
5916              var left = rect.left;
5917              var right = rect.right;
5918              var middle = (rect.left + rect.right) / 2;
5919              var frozen = scroller.querySelectorAll('thead th.rz-frozen-cell');
5920              for (var i = 0; i < frozen.length; i++) {
5921                  var frozenRect = frozen[i].getBoundingClientRect();
5922                  if (frozenRect.width === 0) {
5923                      continue;
5924                  }
5925                  if ((frozenRect.left + frozenRect.right) / 2 < middle) {
5926                      left = Math.max(left, frozenRect.right);
5927                  } else {
5928                      right = Math.min(right, frozenRect.left);
5929                  }
5930              }
5931              var visible = right - left;
5932              if (visible > 0) {
5933                  var zone = Math.max(24, Math.min(80, visible * 0.15));
5934                  var depth = 0;
5935                  if (clientX < left + zone) {
5936                      depth = -(left + zone - clientX);
5937                  } else if (clientX > right - zone) {
5938                      depth = clientX - (right - zone);
5939                  }
5940                  if (depth) {
5941                      var ratio = Math.min(1, Math.abs(depth) / zone);
5942                      speed = Math.sign(depth) * Math.ceil(2 + 18 * ratio * ratio);
5943                  }
5944              }
5945          }
5946          autoScrollSpeed = speed;
5947          if (speed && !autoScrollFrame) {
5948              autoScrollFrame = requestAnimationFrame(autoScrollStep);
5949          }
5950      };
5951
5952      var cleanup = function () {
5953          stopAutoScroll();
5954
5955          var ghost = document.getElementById(id + 'visual');
5956          if (ghost && ghost.parentNode) {
5957              ghost.parentNode.removeChild(ghost);
5958          }
5959          var resizers = cell.parentNode ? cell.parentNode.querySelectorAll('.rz-column-resizer') : [];
5960          for (let i = 0; i < resizers.length; i++) {
5961              resizers[i].style.display = 'block';
5962          }
5963
5964          document.removeEventListener('mousemove', move);
5965          grid.removeEventListener('touchmove', touchmove);
5966          grid.removeEventListener('click', end);
5967          document.removeEventListener('mouseup', end);
5968          document.removeEventListener('touchend', end);
5969          document.removeEventListener('touchcancel', end);
5970
5971          if (Radzen[id + 'reorderCleanup'] === cleanup) {
5972              Radzen[id + 'reorderCleanup'] = null;
5973          }
5974      };
5975
5976      var end = function (e) {
5977          var triggeredByTouch = e && (e.type === 'touchend' || e.type === 'touchcancel');
5978          var touchX = lastTouchX;
5979          var touchY = lastTouchY;
5980          if (triggeredByTouch && e.changedTouches && e.changedTouches[0]) {
5981              touchX = e.changedTouches[0].clientX;
5982              touchY = e.changedTouches[0].clientY;
5983          }
5984
5985          cleanup();
5986
5987          if (triggeredByTouch && gridRef && touchX != null && touchY != null) {
5988              var target = document.elementFromPoint(touchX, touchY);
5989              if (target && grid.contains(target)) {
5990                  var groupHeader = target.closest('.rz-group-header');
5991                  if (groupHeader && grid.contains(groupHeader)) {
5992                      try { gridRef.invokeMethodAsync('RadzenGrid.OnColumnDropToGroup'); } catch { }
5993                      return;
5994                  }
5995                  var th = target.closest('th[data-column-index]');
5996                  if (th && grid.contains(th)) {
5997                      var columnIndex = parseInt(th.getAttribute('data-column-index'), 10);
5998                      if (!isNaN(columnIndex)) {
5999                          try { gridRef.invokeMethodAsync('RadzenGrid.OnColumnReorderEnded', columnIndex); } catch { }
6000                      }
6001                  }
6002              }
6003          }
6004      };
6005
6006      var move = function (e) {
6007          if (e.buttons === 0 || !grid.isConnected) {
6008              cleanup();
6009              return;
6010          }
6011
6012          var ghost = document.getElementById(id + 'visual');
6013          if (ghost) {
6014              ghost.style.display = 'block';
6015
6016              if (/Edge/.test(navigator.userAgent)) {
6017                  var scrollLeft = document.body.scrollLeft;
6018                  var scrollTop = document.body.scrollTop;
6019              } else {
6020                  var scrollLeft = document.documentElement.scrollLeft;
6021                  var scrollTop = document.documentElement.scrollTop;
6022              }
6023
6024              ghost.style.top = e.clientY + scrollTop + 10 + 'px';
6025              ghost.style.left = e.clientX + scrollLeft + 10 + 'px';
6026          }
6027          updateAutoScroll(e.clientX, e.clientY);
6028      };
6029
6030      var touchmove = function (e) {
6031          if (e.touches && e.touches[0]) {
6032              lastTouchX = e.touches[0].clientX;
6033              lastTouchY = e.touches[0].clientY;
6034              move({ clientX: e.touches[0].clientX, clientY: e.touches[0].clientY });
6035              if (e.cancelable) {
6036                  e.preventDefault();
6037              }
6038          }
6039      };
6040
6041      Radzen[id + 'reorderCleanup'] = cleanup;
6042
6043      grid.addEventListener('click', end);
6044      document.addEventListener('mouseup', end);
6045      document.addEventListener('touchend', end, { passive: true });
6046      document.addEventListener('touchcancel', end, { passive: true });
6047      document.addEventListener('mousemove', move);
6048      grid.addEventListener('touchmove', touchmove, { passive: false });
6049  },
6050  stopColumnResize: function (id, grid, columnIndex) {
6051    var resize = Radzen[id + 'columnResize'];
6052    if (resize) {
6053        resize.mouseUpHandler();
6054    }
6055  },
6056  // A grid rendered inside another grid's row-detail template is a descendant of it, so every lookup
6057  // here is scoped to this grid's own table and to a row's own cells. Descendant queries reach the
6058  // inner grid and pin its cells to the outer grid's offsets.
6059  frozenCellsOf: function(row, side) {
6060      var found = [];
6061      if (!row) return found;
6062      var prefix = 'rz-frozen-cell-' + side;
6063      for (var i = 0; i < row.children.length; i++) {
6064          var cell = row.children[i];
6065          if (cell.classList.contains(prefix)
6066              || cell.classList.contains(prefix + '-end')
6067              || cell.classList.contains(prefix + '-inner')) {
6068              found.push(cell);
6069          }
6070      }
6071      return found;
6072  },
6073  updateFrozenColumnPositions: function(gridElement) {
6074      if (!gridElement) return;
6075      // This grid's own table. querySelector returns the first in document order, which is this
6076      // grid's rather than one nested inside it.
6077      var table = gridElement.querySelector('.rz-grid-table');
6078      if (!table) return;
6079      // Get frozen cell positions from the header row first, then apply to all rows
6080      var headerRow = table.querySelector(':scope > thead > tr');
6081      if (!headerRow) return;
6082      var leftHeaderCells = Radzen.frozenCellsOf(headerRow, 'left');
6083      var rightHeaderCells = Radzen.frozenCellsOf(headerRow, 'right');
6084      // Calculate offsets from header widths
6085      var leftOffsets = [];
6086      var offset = 0;
6087      for (var i = 0; i < leftHeaderCells.length; i++) {
6088          leftOffsets.push(offset);
6089          offset += leftHeaderCells[i].getBoundingClientRect().width;
6090      }
6091      var rightOffsets = [];
6092      offset = 0;
6093      for (var i = rightHeaderCells.length - 1; i >= 0; i--) {
6094          rightOffsets[i] = offset;
6095          offset += rightHeaderCells[i].getBoundingClientRect().width;
6096      }
6097      // Apply offsets to this grid's own rows
6098      var rows = table.querySelectorAll(':scope > thead > tr, :scope > tbody > tr, :scope > tfoot > tr');
6099      for (var r = 0; r < rows.length; r++) {
6100          var row = rows[r];
6101          var leftCells = Radzen.frozenCellsOf(row, 'left');
6102          for (var i = 0; i < leftCells.length && i < leftOffsets.length; i++) {
6103              leftCells[i].style.setProperty('inset-inline-start', leftOffsets[i] + 'px');
6104          }
6105          var rightCells = Radzen.frozenCellsOf(row, 'right');
6106          for (var i = 0; i < rightCells.length && i < rightOffsets.length; i++) {
6107              rightCells[i].style.setProperty('inset-inline-end', rightOffsets[i] + 'px');
6108          }
6109      }
6110  },
6111  startColumnResize: function(id, grid, columnIndex, clientX) {
6112      var el = document.getElementById(id + '-resizer');
6113      var resizeKey = id + 'columnResize';
6114      var cell = el.parentNode.parentNode;
6115      var col = document.getElementById(id + '-col');
6116      var gridElement = cell.closest('.rz-data-grid');
6117      var hasFrozenColumns = gridElement != null && gridElement.querySelector('.rz-frozen-cell') != null;
6118      var table = cell.closest('.rz-grid-table');
6119      var colgroup = table ? table.querySelector(':scope > colgroup') : null;
6120      var allColumns = colgroup ? Array.from(colgroup.children) : [];
6121      var headerCells = cell.parentNode.children.length === allColumns.length
6122          ? Array.from(cell.parentNode.children)
6123          : [];
6124      var pinned = allColumns.map(function (column) {
6125          return column === col || !!column.style.width || !column.id.endsWith('-col');
6126      });
6127      var columns = [];
6128      var bounds = [];
6129      allColumns.forEach(function (column, index) {
6130          if (!column.id.endsWith('-col')) return;
6131          var style = getComputedStyle(headerCells[index] || column);
6132          columns.push(column);
6133          bounds.push({
6134              min: parseFloat(style.minWidth) || 0,
6135              max: parseFloat(style.maxWidth),
6136              pinned: pinned[index]
6137          });
6138      });
6139      var cellStyle = getComputedStyle(cell);
6140      var minWidth = parseFloat(cellStyle.minWidth) || 0;
6141      var maxWidth = parseFloat(cellStyle.maxWidth);
6142      var declaredWidths = allColumns.map(column => column.style.width);
6143      var columnWidths = allColumns.map(column => column.getBoundingClientRect().width);
6144      allColumns.forEach(function (column, index) {
6145          if (pinned[index]) {
6146              column.style.width = columnWidths[index] + 'px';
6147          }
6148      });
6149      Radzen[resizeKey] = {
6150          clientX: clientX,
6151          width: cell.getBoundingClientRect().width,
6152          resized: false,
6153          mouseUpHandler: function (e) {
6154              var resize = Radzen[resizeKey];
6155
6156              if (!resize) return;
6157
6158              if (!resize.resized) {
6159                  allColumns.forEach(function (column, index) {
6160                      column.style.width = declaredWidths[index];
6161                  });
6162              }
6163              else {
6164                  var widths = columns.map(function (column, index) {
6165                      if (!bounds[index].pinned) {
6166                          return 0;
6167                      }
6168
6169                      var width = Math.max(parseFloat(column.style.width), bounds[index].min);
6170
6171                      return Number.isNaN(bounds[index].max) ? width : Math.min(width, bounds[index].max);
6172                  });
6173
6174                  try {
6175                      if (columns.length) {
6176                          grid.invokeMethodAsync('RadzenGrid.OnColumnsResized', columnIndex, widths[columnIndex], widths);
6177                      } else {
6178                          grid.invokeMethodAsync('RadzenGrid.OnColumnResized', columnIndex, cell.getBoundingClientRect().width);
6179                      }
6180                  } catch { }
6181              }
6182
6183              el.style.width = null;
6184              document.removeEventListener('mousemove', resize.mouseMoveHandler);
6185              document.removeEventListener('mouseup', resize.mouseUpHandler);
6186              document.removeEventListener('touchmove', resize.touchMoveHandler)
6187              document.removeEventListener('touchend', resize.mouseUpHandler);
6188              Radzen[resizeKey] = null;
6189          },
6190          mouseMoveHandler: function (e) {
6191              if (Radzen[resizeKey]) {
6192                  var widthFloat = (Radzen[resizeKey].width - (Radzen.isRTL(cell) ? -1 : 1) * (Radzen[resizeKey].clientX - e.clientX));
6193
6194                  if (widthFloat < minWidth) {
6195                      widthFloat = minWidth;
6196                  }
6197
6198                  if (!Number.isNaN(maxWidth) && widthFloat > maxWidth) {
6199                      widthFloat = maxWidth;
6200                  }
6201
6202                  if (widthFloat !== Radzen[resizeKey].width) {
6203                      Radzen[resizeKey].resized = true;
6204                  }
6205
6206                  var width = widthFloat + 'px';
6207
6208                  if (col) {
6209                      col.style.width = width;
6210                  } else {
6211                      cell.style.width = width;
6212                  }
6213
6214                  if (hasFrozenColumns) {
6215                      Radzen.updateFrozenColumnPositions(gridElement);
6216                  }
6217              }
6218          },
6219          touchMoveHandler: function (e) {
6220              if (e.targetTouches[0]) {
6221                  e.preventDefault();
6222                  Radzen[resizeKey].mouseMoveHandler(e.targetTouches[0]);
6223              }
6224          }
6225      };
6226      el.style.width = "100%";
6227      document.addEventListener('mousemove', Radzen[resizeKey].mouseMoveHandler);
6228      document.addEventListener('mouseup', Radzen[resizeKey].mouseUpHandler);
6229      document.addEventListener('touchmove', Radzen[resizeKey].touchMoveHandler, { passive: false })
6230      document.addEventListener('touchend', Radzen[resizeKey].mouseUpHandler);
6231  },
6232      startSplitterResize: function(id,
6233        splitter,
6234        paneId,
6235        paneNextId,
6236        orientation,
6237        clientPos,
6238        minValue,
6239        maxValue,
6240        minNextValue,
6241        maxNextValue) {
6242
6243        var el = document.getElementById(id);
6244        var pane = document.getElementById(paneId);
6245        var paneNext = document.getElementById(paneNextId);
6246        var separator = pane ? document.getElementById(pane.id + '-resize') : null;
6247        var paneLength;
6248        var paneNextLength;
6249        var panePerc;
6250        var paneNextPerc;
6251        var isHOrientation=orientation == 'Horizontal';
6252
6253        var totalLength = 0.0;
6254        Array.from(el.children).forEach(element => {
6255            totalLength += isHOrientation
6256                ? element.getBoundingClientRect().width
6257                : element.getBoundingClientRect().height;
6258        });
6259
6260        if (pane) {
6261            paneLength = isHOrientation
6262                ? pane.getBoundingClientRect().width
6263                : pane.getBoundingClientRect().height;
6264
6265            panePerc = (paneLength / totalLength * 100) + '%';
6266        }
6267
6268        if (paneNext) {
6269            paneNextLength = isHOrientation
6270                ? paneNext.getBoundingClientRect().width
6271                : paneNext.getBoundingClientRect().height;
6272
6273            paneNextPerc = (paneNextLength / totalLength * 100) + '%';
6274        }
6275
6276        function ensurevalue(value) {
6277            if (!value)
6278                return null;
6279
6280            value=value.trim().toLowerCase();
6281
6282            if (value.endsWith("%"))
6283                return totalLength*parseFloat(value)/100;
6284
6285            if (value.endsWith("px"))
6286                return parseFloat(value);
6287
6288            throw 'Invalid value';
6289        }
6290
6291        minValue=ensurevalue(minValue);
6292        maxValue=ensurevalue(maxValue);
6293        minNextValue=ensurevalue(minNextValue);
6294        maxNextValue=ensurevalue(maxNextValue);
6295
6296        Radzen[el] = {
6297            clientPos: clientPos,
6298            panePerc: parseFloat(panePerc),
6299            paneNextPerc: isFinite(parseFloat(paneNextPerc)) ? parseFloat(paneNextPerc) : 0,
6300            paneLength: paneLength,
6301            paneNextLength: isFinite(paneNextLength) ? paneNextLength : 0,
6302            mouseUpHandler: function(e) {
6303                if (Radzen[el]) {
6304                    try { splitter.invokeMethodAsync(
6305                        'RadzenSplitter.OnPaneResized',
6306                        parseInt(pane.getAttribute('data-index')),
6307                        parseFloat(pane.style.flexBasis),
6308                        paneNext ? parseInt(paneNext.getAttribute('data-index')) : null,
6309                        paneNext ? parseFloat(paneNext.style.flexBasis) : null
6310                    ); } catch { }
6311
6312                    document.removeEventListener('pointerup', Radzen[el].mouseUpHandler);
6313                    document.removeEventListener('pointercancel', Radzen[el].mouseUpHandler);
6314                    document.removeEventListener('pointermove', Radzen[el].mouseMoveHandler);
6315                    el.removeEventListener('touchmove', preventDefaultAndStopPropagation);
6316                    Radzen[el] = null;
6317                }
6318            },
6319            mouseMoveHandler: function(e) {
6320                if (Radzen[el]) {
6321
6322                    if (e.buttons === 0) {
6323                        Radzen[el].mouseUpHandler(e);
6324                        return;
6325                    }
6326
6327                    try { splitter.invokeMethodAsync(
6328                        'RadzenSplitter.OnPaneResizing'
6329                    ); } catch { }
6330
6331                    var spacePerc = Radzen[el].panePerc + Radzen[el].paneNextPerc;
6332                    var spaceLength = Radzen[el].paneLength + Radzen[el].paneNextLength;
6333
6334                    var length = (Radzen[el].paneLength -
6335                        (isHOrientation && Radzen.isRTL(e.target) ? -1 : 1) * (Radzen[el].clientPos - (isHOrientation ? e.clientX : e.clientY)));
6336
6337                    if (length > spaceLength)
6338                        length = spaceLength;
6339
6340                    if (minValue && length < minValue) length = minValue;
6341                    if (maxValue && length > maxValue) length = maxValue;
6342
6343                    if (paneNext) {
6344                        var nextSpace=spaceLength-length;
6345                        if (minNextValue && nextSpace < minNextValue) length = spaceLength-minNextValue;
6346                        if (maxNextValue && nextSpace > maxNextValue) length = spaceLength-maxNextValue;
6347                    }
6348
6349                    var perc = length / Radzen[el].paneLength;
6350                    if (!isFinite(perc)) {
6351                        perc = 1;
6352                        Radzen[el].panePerc = 0.1;
6353                        Radzen[el].paneLength =isHOrientation
6354                            ? pane.getBoundingClientRect().width
6355                            : pane.getBoundingClientRect().height;
6356                    }
6357
6358                    var newPerc =  Radzen[el].panePerc * perc;
6359                    if (newPerc < 0) newPerc = 0;
6360                    if (newPerc > 100) newPerc = 100;
6361
6362                    pane.style.flexBasis = newPerc + '%';
6363                    if (paneNext)
6364                        paneNext.style.flexBasis = (spacePerc - newPerc) + '%';
6365
6366                    if (separator) {
6367                        var valueMin = parseFloat(separator.getAttribute('aria-valuemin')) || 0;
6368                        var valueMax = parseFloat(separator.getAttribute('aria-valuemax'));
6369                        if (!isFinite(valueMax)) valueMax = 100;
6370                        var valueNow = Math.round(newPerc);
6371                        if (valueNow < valueMin) valueNow = valueMin;
6372                        if (valueNow > valueMax) valueNow = valueMax;
6373                        separator.setAttribute('aria-valuenow', valueNow);
6374                    }
6375                }
6376            },
6377            touchMoveHandler: function(e) {
6378                if (e.targetTouches[0]) {
6379                    Radzen[el].mouseMoveHandler(e.targetTouches[0]);
6380                }
6381            }
6382          };
6383
6384        const preventDefaultAndStopPropagation = (ev) => {
6385            ev.preventDefault();
6386            ev.stopPropagation();
6387        };
6388          document.addEventListener('pointerup', Radzen[el].mouseUpHandler);
6389          document.addEventListener('pointercancel', Radzen[el].mouseUpHandler);
6390          document.addEventListener('pointermove', Radzen[el].mouseMoveHandler);
6391          el.addEventListener('touchmove', preventDefaultAndStopPropagation, { passive: false });
6392    },
6393    resizeSplitter(id, e) {
6394        var el = document.getElementById(id);
6395        if (el && Radzen[el]) {
6396            Radzen[el].mouseMoveHandler(e);
6397            if (Radzen[el]) {
6398                Radzen[el].mouseUpHandler(e);
6399            }
6400        }
6401    },
6402    openWaiting: function() {
6403        if (document.documentElement.scrollHeight > document.documentElement.clientHeight) {
6404            document.body.classList.add('no-scroll');
6405        }
6406        if (Radzen.WaitingIntervalId != null) {
6407            clearInterval(Radzen.WaitingIntervalId);
6408        }
6409
6410        setTimeout(function() {
6411                var timerObj = document.getElementsByClassName('rz-waiting-timer');
6412                if (timerObj.length == 0) return;
6413                var timerStart = new Date().getTime();
6414                Radzen.WaitingIntervalId = setInterval(function() {
6415                        if (timerObj == null || timerObj[0] == null) {
6416                            clearInterval(Radzen.WaitingIntervalId);
6417                        } else {
6418                            var time = new Date(new Date().getTime() - timerStart);
6419                            timerObj[0].innerHTML = Math.floor(time / 1000) + "." + Math.floor((time % 1000) / 100);
6420                        }
6421                    },
6422                    100);
6423            },
6424            100);
6425    },
6426    closeWaiting: function() {
6427        document.body.classList.remove('no-scroll');
6428        if (Radzen.WaitingIntervalId != null) {
6429            clearInterval(Radzen.WaitingIntervalId);
6430            Radzen.WaitingIntervalId = null;
6431        }
6432    },
6433    toggleDictation: function (componentRef, language) {
6434        var restarts = 0;
6435
6436        function start() {
6437            const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
6438
6439            if (!SpeechRecognition) {
6440                return;
6441            }
6442
6443            radzenRecognition = new SpeechRecognition();
6444            radzenRecognition.componentRef = componentRef;
6445            radzenRecognition.continuous = true;
6446
6447            if (language) {
6448                radzenRecognition.lang = language;
6449            }
6450
6451            radzenRecognition.onresult = function (event) {
6452                if (event.results.length < 1) {
6453                    return;
6454                }
6455
6456                restarts = 0;
6457
6458                let current = event.results[event.results.length - 1][0]
6459                let result = current.transcript;
6460
6461                try { suppressDisposed(componentRef.invokeMethodAsync("OnResult", result)); } catch { }
6462            };
6463            radzenRecognition.onerror = function (event) {
6464                if (event.error == 'not-allowed' || event.error == 'service-not-allowed' || event.error == 'audio-capture' || event.error == 'language-not-supported') {
6465                    this.stopRequested = true;
6466                }
6467            };
6468            radzenRecognition.onend = function (event) {
6469                if (!this.stopRequested && restarts++ < 10) {
6470                    start();
6471                    return;
6472                }
6473
6474                if (this == radzenRecognition) {
6475                    radzenRecognition = null;
6476                }
6477
6478                try { suppressDisposed(componentRef.invokeMethodAsync("StopRecording")); } catch { }
6479            };
6480            radzenRecognition.start();
6481        }
6482
6483        if (radzenRecognition) {
6484            radzenRecognition.stopRequested = true;
6485            if (radzenRecognition.componentRef._id != componentRef._id) {
6486                radzenRecognition.addEventListener('end', function () { start(); });
6487            }
6488            radzenRecognition.stop();
6489        } else {
6490            start();
6491        }
6492    },
6493    openChartTooltip: function (chart, x, y, id, instance, callback) {
6494        var existing = document.getElementById(id);
6495        var wasOpen = existing && existing.style.display != 'none' && existing.offsetParent != null;
6496
6497        Radzen.closeTooltip(id);
6498
6499        var chartRect = chart.getBoundingClientRect();
6500        x = Math.max(2, chartRect.left + x);
6501        y = Math.max(2, chartRect.top + y);
6502        Radzen.openPopup(chart, id, false, null, x, y, instance, callback, true, false, false);
6503
6504        var popup = document.getElementById(id);
6505        if (!popup) {
6506            return;
6507        }
6508        var tooltipContent = popup.children[0];
6509
6510        var inner = tooltipContent.querySelector('.rz-chart-tooltip-content');
6511        if (inner) {
6512            inner.classList.remove('rz-chart-tooltip-enter');
6513            if (!wasOpen) {
6514                void inner.offsetWidth;
6515                inner.classList.add('rz-chart-tooltip-enter');
6516            }
6517        }
6518        var tooltipContentRect = tooltipContent.getBoundingClientRect();
6519        var tooltipContentClassName = 'rz-top-chart-tooltip';
6520        if (y - tooltipContentRect.height < 0) {
6521            tooltipContentClassName = 'rz-bottom-chart-tooltip';
6522        }
6523        tooltipContent.classList.remove('rz-top-chart-tooltip');
6524        tooltipContent.classList.remove('rz-bottom-chart-tooltip');
6525        tooltipContent.classList.add(tooltipContentClassName);
6526    },
6527    createScrollListener: function (ref, selectors, selector) {
6528        let currentSelector;
6529        const container = selector ? document.querySelector(selector) : document.documentElement;
6530        const elements = selectors.map(document.querySelector, document);
6531
6532        let rafId = null;
6533
6534        const scrollHandler = () => {
6535            if (rafId !== null) {
6536                return;
6537            }
6538
6539            rafId = requestAnimationFrame(() => {
6540                rafId = null;
6541
6542                const isHtml = !selector || container.tagName === 'HTML';
6543                const threshold = isHtml ? 10 : container.getBoundingClientRect().top + 10;
6544                const atBottom = isHtml
6545                    ? window.scrollY > 0 && (window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight - 2
6546                    : container.scrollTop > 0 && (container.scrollTop + container.clientHeight) >= container.scrollHeight - 2;
6547
6548                let match;
6549
6550                if (atBottom) {
6551                    for (let i = selectors.length - 1; i >= 0; i--) {
6552                        if (elements[i]) {
6553                            match = selectors[i];
6554                            break;
6555                        }
6556                    }
6557                } else {
6558                    for (let i = elements.length - 1; i >= 0; i--) {
6559                        const element = elements[i];
6560
6561                        if (!element) {
6562                            continue;
6563                        }
6564
6565                        if (element.getBoundingClientRect().top <= threshold) {
6566                            match = selectors[i];
6567                            break;
6568                        }
6569                    }
6570                }
6571
6572                if (match !== currentSelector) {
6573                    currentSelector = match;
6574                    try {
6575                        ref.invokeMethodAsync('ScrollIntoView', currentSelector);
6576                    } catch {
6577                    }
6578                }
6579            });
6580        };
6581
6582        const scrollTarget = selector ? container : window;
6583        scrollTarget.addEventListener('scroll', scrollHandler, { passive: true });
6584        window.addEventListener('resize', scrollHandler, { passive: true });
6585
6586        scrollHandler();
6587
6588        return {
6589            dispose() {
6590                scrollTarget.removeEventListener('scroll', scrollHandler);
6591                window.removeEventListener('resize', scrollHandler);
6592            }
6593        };
6594    },
6595    setTheme: function (href, wcagHref) {
6596      const theme = document.getElementById('radzen-theme-link');
6597      if (theme && theme.sheet && theme.sheet.href === theme.href && theme.href != href) {
6598        theme.href = href;
6599      }
6600
6601      let wcagTheme = document.getElementById('radzen-wcag-theme-link');
6602
6603      if (!wcagTheme && wcagHref) {
6604        wcagTheme = document.createElement('link');
6605        wcagTheme.id = 'radzen-wcag-theme-link';
6606        wcagTheme.rel = 'stylesheet';
6607        wcagTheme.href = wcagHref;
6608        theme.parentNode.insertBefore(wcagTheme, theme.nextSibling);
6609      } else if (wcagTheme && wcagTheme.href != wcagHref) {
6610        if (!wcagHref) {
6611          wcagTheme.parentNode.removeChild(wcagTheme);
6612          return;
6613        } else {
6614          wcagTheme.href = wcagHref;
6615        }
6616      }
6617    },
6618    createDraggable: function(element, ref, onDragStart) {
6619      function handleDragStart(e) {
6620        e.dataTransfer.setData('', e.target.id);
6621        try { suppressDisposed(ref.invokeMethodAsync(onDragStart)); } catch { }
6622      }
6623      element.draggable = true;
6624      element.addEventListener('dragstart', handleDragStart);
6625      return {
6626        dispose() {
6627          element.removeEventListener('dragstart', handleDragStart);
6628        }
6629      };
6630    }
6631};
6632
6633Radzen.registerFabMenu = function(element, dotnet){
6634  if(!element) return;
6635  if(element.__rzOutsideClickHandler){
6636    document.removeEventListener('click', element.__rzOutsideClickHandler);
6637    delete element.__rzOutsideClickHandler;
6638  }
6639  const handler = function(e){
6640    if(!element.contains(e.target)){
6641      try { suppressDisposed(dotnet.invokeMethodAsync('CloseAsync')); } catch { }
6642    }
6643  };
6644  element.__rzOutsideClickHandler = handler;
6645  document.addEventListener('click', handler);
6646};
6647Radzen.unregisterFabMenu = function(element){
6648  if(!element) return;
6649  const handler = element.__rzOutsideClickHandler;
6650  if(handler){
6651    document.removeEventListener('click', handler);
6652    delete element.__rzOutsideClickHandler;
6653  }
6654};
6655Radzen.chatIsNearBottom = function(container, threshold) {
6656  if (!container) return true;
6657  return container.scrollHeight - container.scrollTop - container.clientHeight <= (threshold || 50);
6658};
6659Radzen.chatScrollToBottom = function(container) {
6660  if (!container) return;
6661  container.scrollTop = container.scrollHeight;
6662};
6663Radzen.noop = function() {};
6664Radzen.getCookies = function() {
6665  return document.cookie;
6666};
6667Radzen.setCookie = function(cookie) {
6668  document.cookie = cookie;
6669};
6670Radzen.scrollToTop = function(elementId) {
6671  try { var el = document.getElementById(elementId); if (el) el.scrollTop = 0; } catch(e) {}
6672};
6673Radzen.scrollIntoView = function(selector) {
6674  var el = document.querySelector(selector);
6675  if (el) el.scrollIntoView();
6676};
6677Radzen.delayedFocus = function(elementId, delay) {
6678  setTimeout(function() { var el = document.getElementById(elementId); if (el) el.focus(); }, delay || 200);
6679};
6680Radzen.invokeRadzenCallback = function(key) {
6681  var fn = Radzen[key];
6682  if (typeof fn === 'function') fn();
6683};
6684Radzen.chatScrollAfterRender = function(selector, delay) {
6685  setTimeout(function() {
6686    var container = document.querySelector(selector);
6687    if (container) container.scrollTop = container.scrollHeight;
6688  }, delay || 100);
6689};
6690Radzen.createAutoComplete = function(el, popupId, openOnFocus, instance, openCallback, closeCallback) {
6691  if (!el) return { dispose: function() {} };
6692  var input = el.querySelector('input.rz-autocomplete-input, textarea.rz-autocomplete-input');
6693  var list = el.querySelector('.rz-autocomplete-list');
6694  if (!input) return { dispose: function() {} };
6695  function open() {
6696    var popup = document.getElementById(popupId);
6697    var wasOpen = popup && popup.style.display == 'block';
6698    Radzen.openPopup(input.parentNode, popupId, true, null, null, null, instance, closeCallback);
6699    if (!wasOpen && instance && openCallback) {
6700      try { suppressDisposed(instance.invokeMethodAsync(openCallback)); } catch { }
6701    }
6702  }
6703  function onInput() { open(); }
6704  function onFocus() { open(); }
6705  function clearActive() { Radzen.activeElement = null; }
6706  input.addEventListener('input', onInput);
6707  input.addEventListener('blur', clearActive);
6708  if (openOnFocus) input.addEventListener('focus', onFocus);
6709  if (list) list.addEventListener('mousedown', clearActive);
6710  return { dispose: function() {
6711    input.removeEventListener('input', onInput);
6712    input.removeEventListener('blur', clearActive);
6713    if (openOnFocus) input.removeEventListener('focus', onFocus);
6714    if (list) list.removeEventListener('mousedown', clearActive);
6715  }};
6716};
6717Radzen.createDataGrid = function(el) {
6718  if (!el) return { dispose: function() {} };
6719  function onClick(e) {
6720    var label = e.target.closest('.rz-cell-filter-label');
6721    if (label) { e.preventDefault(); }
6722    var btn = e.target.closest('button[aria-haspopup][aria-controls]');
6723    if (btn && !btn.closest('.rz-datepicker') && el.contains(btn) && label && label.contains(btn) && btn.getAttribute('aria-controls')) {
6724      Radzen.togglePopup(btn.parentNode, btn.getAttribute('aria-controls'));
6725    }
6726  }
6727  function onMousedown(e) {
6728    var btn = e.target.closest('.rz-apply-filter');
6729    if (btn) { Radzen.blur(btn, e); }
6730  }
6731  el.addEventListener('click', onClick);
6732  el.addEventListener('mousedown', onMousedown);
6733  return { dispose: function() {
6734    el.removeEventListener('click', onClick);
6735    el.removeEventListener('mousedown', onMousedown);
6736  }};
6737};
6738Radzen.createDropDown = function(el) {
6739  if (!el) return { dispose: function() {} };
6740  function onMousedown() { Radzen.activeElement = null; }
6741  function onFilterClick(e) {
6742    var input = e.target.closest('.rz-multiselect-filter-container input');
6743    if (input) { e.preventDefault(); e.stopPropagation(); }
6744  }
6745  el.addEventListener('mousedown', onMousedown);
6746  el.addEventListener('click', onFilterClick, true);
6747  return { dispose: function() {
6748    el.removeEventListener('mousedown', onMousedown);
6749    el.removeEventListener('click', onFilterClick, true);
6750  }};
6751};
6752Radzen.registerProfileMenuClickAway = function (el, ref) {
6753  if (!el) return;
6754  Radzen.unregisterProfileMenuClickAway(el);
6755  el.profileMenuClickAway = function (e) {
6756    if (!document.contains(el)) {
6757      Radzen.unregisterProfileMenuClickAway(el);
6758      return;
6759    }
6760    if (el.contains(e.target)) {
6761      var item = e.target.closest('.rz-navigation-item');
6762      if (item && item.querySelector('.rz-navigation-menu')) {
6763        return;
6764      }
6765    }
6766    try { suppressDisposed(ref.invokeMethodAsync('CloseOnClickAway')); } catch { }
6767  };
6768  document.addEventListener('click', el.profileMenuClickAway);
6769};
6770Radzen.unregisterProfileMenuClickAway = function (el) {
6771  if (el && el.profileMenuClickAway) {
6772    document.removeEventListener('click', el.profileMenuClickAway);
6773    delete el.profileMenuClickAway;
6774  }
6775};
6776Radzen.createFileInput = function(el) {
6777  if (!el) return { dispose: function() {} };
6778  var choose = el.querySelector('.rz-fileupload-choose');
6779  var fileInput = el.querySelector('input[type=file]');
6780  function onKeydown(e) {
6781    if (e.keyCode === 32 || e.keyCode === 13) {
6782      e.preventDefault();
6783      var child = choose.firstElementChild;
6784      if (child) child.click();
6785    }
6786  }
6787  function onStopKeydown(e) { e.stopPropagation(); }
6788  function onChange(e) { Radzen.uploadInputChange(e, null, false, false, true); }
6789  if (choose) choose.addEventListener('keydown', onKeydown);
6790  if (fileInput) {
6791    fileInput.addEventListener('keydown', onStopKeydown);
6792    fileInput.addEventListener('change', onChange);
6793  }
6794  return { dispose: function() {
6795    if (choose) choose.removeEventListener('keydown', onKeydown);
6796    if (fileInput) {
6797      fileInput.removeEventListener('keydown', onStopKeydown);
6798      fileInput.removeEventListener('change', onChange);
6799    }
6800  }};
6801};
6802Radzen.createMask = function(el, id, mask, pattern, charPattern) {
6803  if (!el) return { dispose: function() {} };
6804  function onInput() { Radzen.mask(id, mask, pattern, charPattern); }
6805  el.addEventListener('input', onInput);
6806  return { dispose: function() { el.removeEventListener('input', onInput); }};
6807};
6808Radzen.createNumeric = function(el, isInteger, separator, min, max, isNullable) {
6809  if (!el) return { dispose: function() {} };
6810  var input = el.querySelector('.rz-numeric-input');
6811  if (!input) return { dispose: function() {} };
6812  function onKeypress(e) { Radzen.numericKeyPress(e, isInteger, separator); }
6813  function onBlur(e) { Radzen.numericOnInput(e, min, max, isNullable); }
6814  function onPaste(e) { Radzen.numericOnPaste(e, min, max); }
6815  input.addEventListener('keypress', onKeypress);
6816  if (min !== null || max !== null) {
6817    input.addEventListener('blur', onBlur);
6818    input.addEventListener('paste', onPaste);
6819  }
6820  return { dispose: function() {
6821    input.removeEventListener('keypress', onKeypress);
6822    input.removeEventListener('blur', onBlur);
6823    input.removeEventListener('paste', onPaste);
6824  }};
6825};
6826Radzen.menuClick = function (e) {
6827  var item = e.target.closest('.rz-navigation-item-wrapper');
6828  if (!item) return;
6829  var menu = item.closest('[data-click-to-open]');
6830  if (!menu) return;
6831  var clickToOpen = menu.getAttribute('data-click-to-open') === 'true';
6832  var navItem = item.closest('.rz-navigation-item');
6833  var hasChildren = navItem && navItem.querySelector('.rz-navigation-menu');
6834  if (clickToOpen || hasChildren) {
6835    Radzen.toggleMenuItem(item);
6836  } else {
6837    Radzen.toggleMenuItem(item, e, false);
6838  }
6839};
6840document.addEventListener('click', Radzen.menuClick);
6841Radzen.createMenu = function(el, clickToOpen) {
6842  if (!el) return { dispose: function() {} };
6843  var hoverItems = [];
6844  if (!clickToOpen) {
6845    el.querySelectorAll('.rz-navigation-item').forEach(function(navItem) {
6846      if (!navItem.querySelector('.rz-navigation-menu')) return;
6847      var li = navItem.closest('li') || navItem;
6848      function onEnter(e) { Radzen.toggleMenuItem(li, e, true, false); }
6849      function onLeave(e) { Radzen.toggleMenuItem(li, e, false, false); }
6850      li.addEventListener('mouseenter', onEnter);
6851      li.addEventListener('mouseleave', onLeave);
6852      hoverItems.push({ el: li, onEnter: onEnter, onLeave: onLeave });
6853    });
6854  }
6855  return { dispose: function() {
6856    hoverItems.forEach(function(h) {
6857      h.el.removeEventListener('mouseenter', h.onEnter);
6858      h.el.removeEventListener('mouseleave', h.onLeave);
6859    });
6860  }};
6861};
6862Radzen.createUpload = function(el, url, auto, multiple, parameterName, method, stream) {
6863  if (!el) return { dispose: function() {} };
6864  var choose = el.querySelector('.rz-fileupload-choose');
6865  var fileInput = el.querySelector('input[type=file]');
6866  function onKeydown(e) {
6867    if (e.keyCode === 32 || e.keyCode === 13) {
6868      e.preventDefault();
6869      var child = choose.firstElementChild;
6870      if (child) child.click();
6871    }
6872  }
6873  function onStopKeydown(e) { e.stopPropagation(); }
6874  function onChange(e) {
6875    Radzen.uploadInputChange(e, url, auto, multiple, true, parameterName, method, stream);
6876  }
6877  function onImgError() { this.style.display = 'none'; }
6878  if (choose) choose.addEventListener('keydown', onKeydown);
6879  if (fileInput) {
6880    fileInput.addEventListener('keydown', onStopKeydown);
6881    if (url) fileInput.addEventListener('change', onChange);
6882  }
6883  el.querySelectorAll('.rz-fileupload-row img').forEach(function(img) {
6884    img.addEventListener('error', onImgError);
6885  });
6886  return { dispose: function() {
6887    if (choose) choose.removeEventListener('keydown', onKeydown);
6888    if (fileInput) {
6889      fileInput.removeEventListener('keydown', onStopKeydown);
6890      if (url) fileInput.removeEventListener('change', onChange);
6891    }
6892    el.querySelectorAll('.rz-fileupload-row img').forEach(function(img) {
6893      img.removeEventListener('error', onImgError);
6894    });
6895  }};
6896};
6897class Spreadsheet {
6898  constructor(element, dotNetRef, shortcuts) {
6899    this.element = element;
6900    this.dotNetRef = dotNetRef;
6901    this.shortcuts = shortcuts || {}; // map of key -> isGlobal (true = global, false = grid-only)
6902    this.rtl = Radzen.isRTL(element);
6903    this.pendingKeys = null;
6904    this.element.addEventListener('keydown', this.onKeyDown);
6905    this.element.addEventListener('focusin', this.onFocusIn);
6906    this.element.addEventListener('pointerdown', this.onPointerDown);
6907    this.element.addEventListener('dblclick', this.onDoubleClick);
6908    this.element.addEventListener('contextmenu', this.onContextMenu);
6909    addEventListener('paste', this.onPaste);
6910    addEventListener('copy', this.onCopy);
6911  }
6912
6913  onPaste = (e) => {
6914    if (this.isActive(e.target)) {
6915      var text = e.clipboardData.getData('text/plain');
6916      this.dotNetRef.invokeMethodAsync('OnPasteAsync', text);
6917    }
6918  }
6919
6920  onCopy = async (e) => {
6921    if (this.isActive(e.target)) {
6922      this.dotNetRef.invokeMethodAsync('OnCopyAsync');
6923    }
6924  }
6925
6926  isActive = (target) => {
6927    return target == this.element || this.element.contains(target);
6928  }
6929
6930  isCellEditorTextEntry = (target) => {
6931    if (!target.isContentEditable && target.tagName !== 'INPUT' && target.tagName !== 'TEXTAREA') {
6932      return false;
6933    }
6934
6935    const cellEditor = target.closest('.rz-spreadsheet-cell-editor');
6936    return cellEditor != null && this.element.contains(cellEditor);
6937  }
6938
6939  copyToClipboard = async (text) => {
6940    try {
6941      await navigator.clipboard.writeText(text);
6942    } catch {
6943      // Clipboard API failed (e.g. user activation expired)
6944    }
6945  }
6946
6947  readClipboardText = async () => {
6948    try {
6949      return await navigator.clipboard.readText();
6950    } catch {
6951      return null;
6952    }
6953  }
6954
6955  onPointerDown = async (e) => {
6956    if (e.button != 0) return;
6957
6958    this.pendingKeys = null;
6959
6960    this.rtl = Radzen.isRTL(this.element);
6961
6962    if (e.target.matches('.rz-spreadsheet-autofill-handle')) {
6963      addEventListener('pointermove', this.onAutofillPointerMove);
6964      addEventListener('pointerup', this.onAutofillPointerUp);
6965      this.dotNetRef.invokeMethodAsync('OnAutofillPointerDownAsync', this.toEventArgs(e));
6966      e.preventDefault();
6967      e.stopPropagation();
6968      return;
6969    }
6970
6971    if (e.target.matches('.rz-spreadsheet-cell')) {
6972      const cell = e.target;
6973      const row = +cell.dataset.row;
6974      const column = +cell.dataset.column;
6975      addEventListener('pointerup', this.onCellPointerUp);
6976      addEventListener('pointermove', this.onCellPointerMove);
6977      if (!(await this.dotNetRef.invokeMethodAsync('OnCellPointerDownAsync', { row, c
6977olumn, pointer: this.toEventArgs(e) }))) {
6978        removeEventListener('pointermove', this.onCellPointerMove);
6979      }
6980    } else if (e.target.matches('.rz-spreadsheet-row-header')) {
6981      const row = +e.target.dataset.row;
6982      addEventListener('pointerup', this.onRowPointerUp);
6983      addEventListener('pointermove', this.onRowPointerMove);
6984      if (!(await this.dotNetRef.invokeMethodAsync('OnRowPointerDownAsync', { row, pointer: this.toEventArgs(e) }))) {
6985        removeEventListener('pointermove', this.onRowPointerMove);
6986      }
6987    } else if (e.target.matches('.rz-spreadsheet-column-header')) {
6988      const column = +e.target.dataset.column;
6989      addEventListener('pointerup', this.onColumnPointerUp);
6990      addEventListener('pointermove', this.onColumnPointerMove);
6991      if (!(await this.dotNetRef.invokeMethodAsync('OnColumnPointerDownAsync', { column, pointer: this.toEventArgs(e) }))) {
6992        removeEventListener('pointermove', this.onColumnPointerMove);
6993      }
6994    } else if (e.target.matches('.rz-spreadsheet-resize-handle')) {
6995      const direction = e.target.dataset.direction;
6996      addEventListener('pointermove', this.onDrawingResizeMove);
6997      addEventListener('pointerup', this.onDrawingResizeUp);
6998      if (!(await this.dotNetRef.invokeMethodAsync('OnDrawingResizePointerDownAsync', { direction, pointer: this.toEventArgs(e) }))) {
6999        removeEventListener('pointermove', this.onDrawingResizeMove);
7000        removeEventListener('pointerup', this.onDrawingResizeUp);
7001      }
7002      e.preventDefault();
7003      e.stopPropagation();
7004    } else if (e.target.closest('.rz-spreadsheet-image') || e.target.closest('.rz-spreadsheet-chart')) {
7005      addEventListener('pointermove', this.onDrawingMoveMove);
7006      addEventListener('pointerup', this.onDrawingMoveUp);
7007      this.dotNetRef.invokeMethodAsync('OnDrawingMovePointerDownAsync', this.toEventArgs(e));
7008      e.preventDefault();
7009    } else if (e.target.matches('.rz-spreadsheet-column-resize-handle')) {
7010      const column = +e.target.dataset.column;
7011        addEventListener('pointermove', this.onColumnResizeMove);
7012        addEventListener('pointerup', this.onColumnResizeUp);
7013      if (!(await this.dotNetRef.invokeMethodAsync('OnColumnResizePointerDownAsync', { column, pointer: this.toEventArgs(e) }))) {
7014        removeEventListener('pointermove', this.onColumnResizeMove);
7015        removeEventListener('pointerup', this.onColumnResizeUp);
7016      }
7017      e.preventDefault();
7018      e.stopPropagation();
7019    } else if (e.target.matches('.rz-spreadsheet-row-resize-handle')) {
7020      const row = +e.target.dataset.row;
7021      addEventListener('pointermove', this.onRowResizeMove);
7022      addEventListener('pointerup', this.onRowResizeUp);
7023      if (!(await this.dotNetRef.invokeMethodAsync('OnRowResizePointerDownAsync', { row, pointer: this.toEventArgs(e) }))) {
7024        removeEventListener('pointermove', this.onRowResizeMove);
7025        removeEventListener('pointerup', this.onRowResizeUp);
7026      }
7027      e.preventDefault();
7028      e.stopPropagation();
7029    }
7030  }
7031
7032  onContextMenu = (e) => {
7033    if (e.target.matches('.rz-spreadsheet-cell')) {
7034      e.preventDefault();
7035      const row = +e.target.dataset.row;
7036      const column = +e.target.dataset.column;
7037      this.dotNetRef.invokeMethodAsync('OnCellContextMenuAsync', { row, column, pointer: this.toEventArgs(e) });
7038    } else if (e.target.matches('.rz-spreadsheet-row-header')) {
7039      e.preventDefault();
7040      const row = +e.target.dataset.row;
7041      this.dotNetRef.invokeMethodAsync('OnRowContextMenuAsync', { row, pointer: this.toEventArgs(e) });
7042    } else if (e.target.matches('.rz-spreadsheet-column-header')) {
7043      e.preventDefault();
7044      const column = +e.target.dataset.column;
7045      this.dotNetRef.invokeMethodAsync('OnColumnContextMenuAsync', { column, pointer: this.toEventArgs(e) });
7046    } else if (e.target === this.element) {
7047      // Keyboard-triggered (Shift+F10 / Menu key) lands on the grid root, not a cell. Suppress the
7048      // browser's native menu; the C# Shift+F10 shortcut opens our menu at the active cell.
7049      e.preventDefault();
7050    }
7051  }
7052
7053  onDoubleClick = (e) => {
7054    if (e.target.matches('.rz-spreadsheet-cell')) {
7055      const cell = e.target;
7056      const row = +cell.dataset.row;
7057      const column = +cell.dataset.column;
7058      this.dotNetRef.invokeMethodAsync('OnCellDoubleClickAsync', { row, column, pointer: this.toEventArgs(e) });
7059    } else if (e.target.matches('.rz-spreadsheet-column-resize-handle')) {
7060      e.preventDefault();
7061      e.stopPropagation();
7062      const column = +e.target.dataset.column;
7063      this.dotNetRef.invokeMethodAsync('OnColumnResizeDoubleClickAsync', { row: 0, column, pointer: this.toEventArgs(e) });
7064    }
7065  }
7066
7067  measureTexts = (items) => {
7068    const probe = document.createElement('div');
7069    probe.className = 'rz-spreadsheet-cell';
7070    probe.style.visibility = 'hidden';
7071    this.element.appendChild(probe);
7072    const style = getComputedStyle(probe);
7073    const baseFont = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
7074    const padding = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0)
7075      + (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0);
7076    const baseFontSize = style.fontSize;
7077    const baseFontFamily = style.fontFamily;
7078    this.element.removeChild(probe);
7079
7080    this.measureCanvas ??= document.createElement('canvas');
7081    const context = this.measureCanvas.getContext('2d');
7082    context.direction = this.rtl ? 'rtl' : 'ltr';
7083
7084    return items.map(item => {
7085      // Invalid font shorthand assignments are silently ignored; reset so they fall back to the base font.
7086      context.font = baseFont;
7087      if (item.bold || item.italic || item.fontSize != null || item.fontFamily) {
7088        const size = item.fontSize != null ? item.fontSize + 'pt' : baseFontSize;
7089        const family = item.fontFamily ? `"${item.fontFamily.replaceAll('"', '\\"')}", ${baseFontFamily}` : baseFontFamily;
7090        context.font = `${item.italic ? 'italic ' : ''}${item.bold ? 'bold ' : ''}${size} ${family}`;
7091      }
7092      const width = context.measureText(item.text).width;
7093      // 3% over-fit absorbs metric differences between canvas and other renderers (e.g. Excel after export).
7094      return Number.isFinite(width) ? Math.ceil(width * 1.03 + padding) : 0;
7095    });
7096  }
7097
7098  onColumnPointerUp = (e) => {
7099    removeEventListener('pointermove', this.onColumnPointerMove);
7100    removeEventListener('pointerup', this.onColumnPointerUp);
7101    this.dotNetRef.invokeMethodAsync('OnSelectionPointerUpAsync');
7102  }
7103
7104  onColumnPointerMove = (e) => {
7105    this.invokeAsync('OnColumnPointerMoveAsync', e);
7106  }
7107
7108  onRowPointerMove = (e) => {
7109    this.invokeAsync('OnRowPointerMoveAsync', e);
7110  }
7111
7112  onRowPointerUp = (e) => {
7113    removeEventListener('pointermove', this.onRowPointerMove);
7114    removeEventListener('pointerup', this.onRowPointerUp);
7115    this.dotNetRef.invokeMethodAsync('OnSelectionPointerUpAsync');
7116  }
7117
7118  onCellPointerMove = (e) => {
7119    this.invokeAsync('OnCellPointerMoveAsync', e);
7120  }
7121
7122  onCellPointerUp = (e) => {
7123    removeEventListener('pointermove', this.onCellPointerMove);
7124    removeEventListener('pointerup', this.onCellPointerUp);
7125    this.dotNetRef.invokeMethodAsync('OnSelectionPointerUpAsync');
7126  }
7127
7128  onKeyDown = (e) => {
7129    if (this.isCellEditorTextEntry(e.target) &&
7130        (e.isComposing || (e.key !== 'Enter' && e.key !== 'Escape' && e.key !== 'Tab' && e.key !== 'F6'))) {
7131      return;
7132    }
7133
7134    let key = '';
7135
7136    if (e.ctrlKey || e.metaKey) {
7137      key += 'Ctrl+';
7138    }
7139
7140    if (e.altKey) {
7141      key += 'Alt+';
7142    }
7143
7144    if (e.shiftKey) {
7145      key += 'Shift+';
7146    }
7147
7148    key += e.code.replace('Key', '').replace('Digit', '').replace('Numpad', '');
7149
7150    // Grid context = focus on THIS spreadsheet's root or inside one of ITS cell/formula editors.
7151    // Scope the editor check to this.element so a sibling spreadsheet on the same page can never
7152    // match. Outside the grid (a toolbar button, sheet tab, ...) only global shortcuts act, so Tab
7153    // etc. stay native and the chrome is keyboard-navigable.
7154    let isGridContext = e.target === this.element;
7155    if (!isGridContext && e.target.closest) {
7156      const editorInput = e.target.closest('.rz-spreadsheet-editor-input');
7157      isGridContext = editorInput != null && this.element.contains(editorInput);
7158    }
7159
7160    const global = this.shortcuts[key]; // true = global, false = grid-only, undefined = not a shortcut
7161    if (global === true || (global === false && isGridContext)) {
7162      e.preventDefault();
7163    }
7164
7165    const printable = global === undefined && !e.ctrlKey && !e.metaKey && !e.altKey &&
7166      e.key.length === 1 && e.target === this.element;
7167
7168    // Prevent default for printable characters when not already editing.
7169    // Without this, the character gets inserted twice: once by StartEdit and
7170    // once by the browser's default insertText when the editor receives focus.
7171    if (printable) {
7172      e.preventDefault();
7173    }
7174
7175    if (printable) {
7176      if (this.pendingKeys != null) {
7177        this.pendingKeys += e.key;
7178        return;
7179      }
7180
7181      this.pendingKeys = '';
7182    } else if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
7183      this.pendingKeys = null;
7184    }
7185
7186    this.dotNetRef.invokeMethodAsync('OnKeyDownAsync', this.toEventArgs(e), isGridContext);
7187  }
7188
7189  onFocusIn = (e) => {
7190    const keys = this.pendingKeys;
7191
7192    this.pendingKeys = null;
7193
7194    const target = e.target;
7195
7196    if (!keys || !(target.matches('.rz-spreadsheet-editor-input') || this.isCellEditorTextEntry(target))) {
7197      return;
7198    }
7199
7200    if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
7201      try {
7202        target.setRangeText(keys, target.value.length, target.value.length, 'end');
7203      } catch {
7204        target.value += keys;
7205      }
7206    } else {
7207      target.innerText += keys;
7208
7209      const range = document.createRange();
7210      range.selectNodeContents(target);
7211      range.collapse(false);
7212
7213      const selection = getSelection();
7214      selection.removeAllRanges();
7215      selection.addRange(range);
7216    }
7217
7218    target.dispatchEvent(new Event('input', { bubbles: true }));
7219  }
7220
7221  // F6 / Shift+F6 cycle focus between the spreadsheet regions in their visible top-to-bottom order:
7222  // ribbon tabs -> active toolbar -> formula bar -> grid -> sheet tabs. This is the advertised escape
7223  // from the grid (WCAG 2.1.2).
7224  focusAdjacent = (forward) => {
7225    // this.element IS this spreadsheet's root (.rz-spreadsheet, role=application). querySelector
7226    // only matches descendants of root, so regions from another spreadsheet on the page are never
7227    // matched.
7228    const root = this.element;
7229    const regions = [
7230      root.querySelector('[role="tablist"], .rz-tabs-nav'),
7231      root.querySelector('.rz-toolbar'),
7232      root.querySelector('.rz-spreadsheet-formula-editor .rz-spreadsheet-editor-input'),
7233      root,
7234      root.querySelector('.rz-spreadsheet-sheet-tabs')
7235    ].filter(r => r);
7236
7237    if (regions.length === 0) {
7238      return;
7239    }
7240
7241    // Regions nest (the ribbon tabview contains the toolbar; the root contains everything), so pick
7242    // the INNERMOST region that contains the focused element - otherwise an outer region captures
7243    // focus that belongs to a nested one and F6 gets stuck cycling between two regions.
7244    const active = document.activeElement;
7245    let index = -1;
7246    let best = null;
7247    regions.forEach((r, i) => {
7248      if ((r === active || r.contains(active)) && (best === null || best.contains(r))) {
7249        best = r;
7250        index = i;
7251      }
7252    });
7253    if (index === -1) {
7254      index = regions.indexOf(root);
7255    }
7256
7257    const next = (index + (forward ? 1 : -1) + regions.length) % regions.length;
7258    this.focusRegion(regions[next]);
7259  }
7260
7261  focusRegion = (region) => {
7262    // Focus the region itself when it is the tab stop: the grid root and the ribbon tablist both have
7263    // tabindex=0 (the tablist owns arrow-key tab switching via aria-activedescendant, so focusing an
7264    // individual tab would break the arrows), and the formula bar is a tabindex=-1 contenteditable.
7265    // Otherwise focus the region's first focusable child (e.g. the first toolbar button).
7266    if (region.tabIndex >= 0 || region.matches('.rz-spreadsheet-editor-input')) {
7267      region.focus();
7268      return;
7269    }
7270
7271    // Skip hidden focusables: inline-rendered but closed popups (color pickers, numeric steppers)
7272    // match the selector yet calling focus() on a hidden element silently fails, which would strand
7273    // the user on the previous region. Pick the first one that is actually rendered.
7274    const focusables = region.querySelectorAll('button:not([disabled]), a[href], input:not([disabled]), [tabindex]:not([tabindex="-1"]), [contenteditable="true"]');
7275    const target = [...focusables].find(el => el.offsetParent !== null || el.getClientRects().length > 0);
7276    (target || region).focus();
7277  }
7278
7279  // Opens the cell context menu at the active cell (Shift+F10 / ContextMenu key). The cell element is
7280  // scoped to this spreadsheet; a synthetic pointer carries its on-screen position.
7281  openCellContextMenu = (row, column) => {
7282    const cell = this.element.querySelector('.rz-spreadsheet-cell[data-row="' + row + '"][data-column="' + column + '"]');
7283    const rect = (cell || this.element).getBoundingClientRect();
7284    const pointer = {
7285      clientX: Math.round(rect.left + 8),
7286      clientY: Math.round(cell ? rect.bottom : rect.top + 8),
7287      button: 0, buttons: 0, pointerType: 'mouse', isPrimary: true
7288    };
7289    this.dotNetRef.invokeMethodAsync('OnCellContextMenuAsync', { row, c
7289olumn, pointer });
7290  }
7291
7292  invokeAsync(name, e) {
7293    this.dotNetRef.invokeMethodAsync(name, this.toEventArgs(e));
7294  }
7295
7296  toEventArgs(e) {
7297    return {
7298      key: e.key,
7299      code: this.rtl && (e.code === 'ArrowLeft' || e.code === 'ArrowRight')
7300        ? (e.code === 'ArrowLeft' ? 'ArrowRight' : 'ArrowLeft')
7301        : e.code,
7302      location: e.location,
7303      repeat: e.repeat,
7304      ctrlKey: e.ctrlKey,
7305      shiftKey: e.shiftKey,
7306      altKey: e.altKey,
7307      metaKey: e.metaKey,
7308      type: e.type,
7309      button: e.button,
7310      buttons: e.buttons,
7311      clientX: this.rtl ? -e.clientX : e.clientX,
7312      clientY: e.clientY,
7313      offsetX: this.rtl ? (e.target?.offsetWidth ?? 0) - e.offsetX : e.offsetX,
7314      offsetY: e.offsetY,
7315      pageX: e.pageX,
7316      pageY: e.pageY,
7317      screenX: e.screenX,
7318      screenY: e.screenY
7319    };
7320  }
7321
7322  dispose() {
7323    this.element.removeEventListener('keydown', this.onKeyDown);
7324    this.element.removeEventListener('focusin', this.onFocusIn);
7325    this.element.removeEventListener('pointerdown', this.onPointerDown);
7326    this.element.removeEventListener('dblclick', this.onDoubleClick);
7327    this.element.removeEventListener('contextmenu', this.onContextMenu);
7328    removeEventListener('paste', this.onPaste);
7329    removeEventListener('copy', this.onCopy);
7330  }
7331
7332  onColumnResizeMove = (e) => {
7333    this.invokeAsync('OnColumnResizePointerMoveAsync', e);
7334  }
7335
7336  onColumnResizeUp = (e) => {
7337    this.invokeAsync('OnColumnResizePointerUpAsync', e);
7338    removeEventListener('pointermove', this.onColumnResizeMove);
7339    removeEventListener('pointerup', this.onColumnResizeUp);
7340  }
7341
7342  onRowResizeMove = (e) => {
7343    this.invokeAsync('OnRowResizePointerMoveAsync', e);
7344  }
7345
7346  onRowResizeUp = (e) => {
7347    this.invokeAsync('OnRowResizePointerUpAsync', e);
7348    removeEventListener('pointermove', this.onRowResizeMove);
7349    removeEventListener('pointerup', this.onRowResizeUp);
7350  }
7351
7352  onDrawingResizeMove = (e) => {
7353    this.invokeAsync('OnDrawingResizePointerMoveAsync', e);
7354  }
7355
7356  onDrawingResizeUp = (e) => {
7357    this.invokeAsync('OnDrawingResizePointerUpAsync', e);
7358    removeEventListener('pointermove', this.onDrawingResizeMove);
7359    removeEventListener('pointerup', this.onDrawingResizeUp);
7360  }
7361
7362  onDrawingMoveMove = (e) => {
7363    this.invokeAsync('OnDrawingMovePointerMoveAsync', e);
7364  }
7365
7366  onDrawingMoveUp = (e) => {
7367    this.invokeAsync('OnDrawingMovePointerUpAsync', e);
7368    removeEventListener('pointermove', this.onDrawingMoveMove);
7369    removeEventListener('pointerup', this.onDrawingMoveUp);
7370  }
7371
7372  onAutofillPointerMove = (e) => {
7373    this.invokeAsync('OnAutofillPointerMoveAsync', e);
7374  }
7375
7376  onAutofillPointerUp = (e) => {
7377    this.dotNetRef.invokeMethodAsync('OnAutofillPointerUpAsync', this.toEventArgs(e));
7378    removeEventListener('pointermove', this.onAutofillPointerMove);
7379    removeEventListener('pointerup', this.onAutofillPointerUp);
7380  }
7381}
7382
7383Radzen.beginRangePickKeyHandler = function (dotNetRef) {
7384  Radzen.endRangePickKeyHandler();
7385  Radzen.rangePickKeyHandler = function (e) {
7386    if (e.key === 'Escape') {
7387      e.preventDefault();
7388      e.stopPropagation();
7389      dotNetRef.invokeMethodAsync('OnEscapeKeyAsync');
7390    }
7391  };
7392  document.addEventListener('keydown', Radzen.rangePickKeyHandler, true);
7393};
7394
7395Radzen.endRangePickKeyHandler = function () {
7396  if (Radzen.rangePickKeyHandler) {
7397    document.removeEventListener('keydown', Radzen.rangePickKeyHandler, true);
7398    Radzen.rangePickKeyHandler = null;
7399  }
7400};
7401
7402class SheetEditor {
7403  constructor(element, value, autoFocus, dotNetRef) {
7404    this.element = element;
7405    this.element.innerText = value;
7406    this.dotNetRef = dotNetRef;
7407    this.element.addEventListener('input', this.onInput);
7408    this.element.addEventListener('keydown', this.onKeyDown);
7409    this.element.addEventListener('blur', this.onBlur);
7410    this.element.addEventListener('focus', this.onFocus);
7411    this.element.addEventListener('paste', this.onPaste);
7412    document.addEventListener('selectionchange', this.onSelectionChange);
7413    if (autoFocus) {
7414      this.focus();
7415    }
7416  }
7417
7418  onPaste = (e) => {
7419    e.preventDefault();
7420    const text = e.clipboardData.getData('text/plain');
7421    const selection = getSelection();
7422    if (!selection.rangeCount) return;
7423    selection.deleteFromDocument();
7424    selection.getRangeAt(0).insertNode(document.createTextNode(text));
7425    selection.collapseToEnd();
7426    this.onInput();
7427  };
7428
7429  onSelectionChange = (e) => {
7430    const selection = getSelection();
7431    if (!selection.focusNode) return;
7432    const inside = selection.focusNode.parentElement == this.element || selection.focusNode == this.element;
7433    let caretPosition = -1;
7434
7435    if (inside && selection.isCollapsed)
7436    {
7437      caretPosition = selection.focusOffset;
7438    }
7439
7440    this.dotNetRef.invokeMethodAsync('OnSelectionChangeAsync', caretPosition);
7441  };
7442
7443  onInput = () => {
7444    // Browsers keep a placeholder <br> in an emptied contenteditable which makes innerText report "\n" for empty content
7445    const text = this.element.innerText;
7446    this.dotNetRef.invokeMethodAsync('OnInputAsync', text == '\n' ? '' : text);
7447  };
7448
7449  setValue = (value, moveCaretTo) => {
7450    this.element.innerText = value;
7451    if (moveCaretTo != null) {
7452      const range = document.createRange();
7453      const el = this.element.childNodes[0];
7454      range.selectNodeContents(el)
7455      range.setStart(el, 0);
7456      range.setEnd(el, moveCaretTo);
7457      range.collapse(false);
7458      const selection = getSelection();
7459      selection.removeAllRanges();
7460      selection.addRange(range);
7461    }
7462  };
7463
7464  onKeyDown = (e) => {
7465    if (e.key == 'Enter' && e.altKey) {
7466      e.preventDefault();
7467      e.stopPropagation();
7468
7469      const range = document.createRange();
7470      range.selectNodeContents(this.element);
7471      range.collapse(false);
7472      range.deleteContents();
7473
7474
7475      let br = document.createElement("br");
7476      range.insertNode(br);
7477
7478      if (!br.previousSibling || br.previousSibling.nodeName != 'BR') {
7479        br = br.cloneNode();
7480        range.insertNode(br);
7481      }
7482
7483      range.setStartAfter(br);
7484      range.setEndAfter(br);
7485
7486      const selection = getSelection();
7487      selection.removeAllRanges();
7488      selection.addRange(range);
7489    } else {
7490       const popup = document.querySelector('.rz-spreadsheet-highlight-popup .rz-state-highlight') != null;
7491       if (popup && (e.key == 'Tab' || e.key == 'ArrowUp' || e.key == 'ArrowDown')) {
7492         e.stopPropagation();
7493         e.preventDefault();
7494         this.dotNetRef.invokeMethodAsync('OnKeyDownAsync', { key: e.key });
7495       } else if (e.key != 'Enter' && e.key != 'Escape' && e.key != 'Tab' && e.key != 'F6') {
7496         e.stopPropagation();
7497       }
7498    }
7499  };
7500
7501  onBlur = (e) => {
7502    if (e.relatedTarget && e.relatedTarget.matches('.rz-spreadsheet-editor-input') && e.relatedTarget.closest('.rz-spreadsheet') == this.element.closest('.rz-spreadsheet')) {
7503      return;
7504    }
7505
7506    this.dotNetRef.invokeMethodAsync('OnBlurAsync');
7507  }
7508
7509  onFocus = () => {
7510    this.dotNetRef.invokeMethodAsync('OnFocusAsync');
7511  }
7512
7513  focus() {
7514    this.element.focus();
7515    const range = document.createRange();
7516    range.selectNodeContents(this.element);
7517    range.collapse(false);
7518
7519    const selection = getSelection();
7520    selection.removeAllRanges();
7521    selection.addRange(range);
7522  }
7523
7524  dispose() {
7525    this.element.removeEventListener('input', this.onInput);
7526    this.element.removeEventListener('keydown', this.onKeyDown);
7527    this.element.removeEventListener('blur', this.onBlur);
7528    this.element.removeEventListener('focus', this.onFocus);
7529    this.element.removeEventListener('paste', this.onPaste);
7530    document.removeEventListener('selectionchange', this.onSelectionChange);
7531  }
7532}
7533
7534
7535
7536
7537Radzen.createSheetEditor = (element, value, autoFocus, dotNetRef) => new SheetEditor(element, value, autoFocus, dotNetRef);
7538Radzen.createSpreadsheet = (element, dotNetRef, shortcuts) => new Spreadsheet(element, dotNetRef, shortcuts);
7539Radzen.createVirtualItemContainer = (scrollable, content, ref) => {
7540  var height = scrollable.clientHeight;
7541  var width = scrollable.clientWidth;
7542  var scrollHeight = scrollable.scrollHeight;
7543  var scrollWidth = scrollable.scrollWidth;
7544
7545  content.addEventListener('mousewheel', function (e) {
7546    scrollable.scrollBy({
7547      top: e.deltaY,
7548      left: e.deltaX
7549    });
7550    e.preventDefault();
7551  });
7552
7553  var rtl = Radzen.isRTL(scrollable);
7554
7555  var inflight = false;
7556
7557  function notifyScroll() {
7558    var scrollTop = scrollable.scrollTop;
7559    // In RTL the native scrollLeft is 0 at the right and negative toward the left;
7560    // report a non-negative logical scroll so the C# layout math stays direction-agnostic.
7561    var scrollLeft = rtl ? -scrollable.scrollLeft : scrollable.scrollLeft;
7562
7563    inflight = true;
7564
7565    ref.invokeMethodAsync('OnScroll', scrollLeft, scrollTop).finally(function () {
7566      inflight = false;
7567
7568      if (scrollTop !== scrollable.scrollTop ||
7569          scrollLeft !== (rtl ? -scrollable.scrollLeft : scrollable.scrollLeft)) {
7570        notifyScroll();
7571      }
7572    });
7573  }
7574
7575  scrollable.addEventListener('scroll', function () {
7576    if (!inflight) {
7577      notifyScroll();
7578    }
7579  });
7580
7581  var observer = new ResizeObserver(function () {
7582    const height = scrollable.clientHeight;
7583    const width = scrollable.clientWidth;
7584
7585    ref.invokeMethodAsync('OnResize', width, height);
7586  });
7587
7588  observer.observe(scrollable);
7589
7590  return { width, height, scrollHeight, scrollWidth };
7591};
7592Radzen.scrollElementTo = (scrollable, left, top) => {
7593  scrollable.scrollTo({ top: top, left: Radzen.isRTL(scrollable) ? -left : left });
7594};
7595Radzen.createFormField = function(el) {
7596  if (!el || typeof el.addEventListener !== 'function') return { dispose: function() {} };
7597  function onFocusIn() { el.classList.add('rz-state-focused'); }
7598  function onFocusOut() { el.classList.remove('rz-state-focused'); }
7599  el.addEventListener('focusin', onFocusIn);
7600  el.addEventListener('focusout', onFocusOut);
7601  return { dispose: function() {
7602    el.removeEventListener('focusin', onFocusIn);
7603    el.removeEventListener('focusout', onFocusOut);
7604  }};
7605};
7606Radzen.createSignaturePad = function(element, ref, strokeColor, strokeWidth, disabled, initialValue) {
7607  var noop = { update: function() {}, clear: function() {}, dispose: function() {} };
7608  if (!element) return noop;
7609
7610  var canvas = element.querySelector('canvas');
7611  if (!canvas) return noop;
7612
7613  var ctx = canvas.getContext('2d');
7614  var drawing = false;
7615  var paths = [];
7616  var currentPath = [];
7617
7618  function syncSize() {
7619    var rect = canvas.getBoundingClientRect();
7620    var w = Math.round(rect.width);
7621    var h = Math.round(rect.height);
7622    if (w > 0 && h > 0 && (canvas.width !== w || canvas.height !== h)) {
7623      canvas.width = w;
7624      canvas.height = h;
7625      redraw();
7626    }
7627  }
7628
7629  function getPointerPos(e) {
7630    var rect = canvas.getBoundingClientRect();
7631    var scaleX = canvas.width / rect.width;
7632    var scaleY = canvas.height / rect.height;
7633    return {
7634      x: (e.clientX - rect.left) * scaleX,
7635      y: (e.clientY - rect.top) * scaleY
7636    };
7637  }
7638
7639  function redraw() {
7640    ctx.clearRect(0, 0, canvas.width, canvas.height);
7641    ctx.strokeStyle = strokeColor;
7642    ctx.lineWidth = strokeWidth;
7643    ctx.lineCap = 'round';
7644    ctx.lineJoin = 'round';
7645    for (var i = 0; i < paths.length; i++) {
7646      var path = paths[i];
7647      if (path.length < 2) continue;
7648      ctx.beginPath();
7649      ctx.moveTo(path[0].x, path[0].y);
7650      for (var j = 1; j < path.length; j++) {
7651        ctx.lineTo(path[j].x, path[j].y);
7652      }
7653      ctx.stroke();
7654    }
7655  }
7656
7657  function loadImage(dataUrl) {
7658    if (!dataUrl) return;
7659    var img = new Image();
7660    img.onload = function() {
7661      ctx.clearRect(0, 0, canvas.width, canvas.height);
7662      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
7663    };
7664    img.src = dataUrl;
7665  }
7666
7667  syncSize();
7668
7669  if (initialValue) {
7670    loadImage(initialValue);
7671  }
7672
7673  var resizeObserver = new ResizeObserver(function() { syncSize(); });
7674  resizeObserver.observe(canvas);
7675
7676  function onPointerDown(e) {
7677    if (disabled) return;
7678    drawing = true;
7679    currentPath = [getPointerPos(e)];
7680    canvas.setPointerCapture(e.pointerId);
7681    e.preventDefault();
7682  }
7683
7684  function onPointerMove(e) {
7685    if (!drawing || disabled) return;
7686    var pos = getPointerPos(e);
7687    currentPath.push(pos);
7688
7689    ctx.strokeStyle = strokeColor;
7690    ctx.lineWidth = strokeWidth;
7691    ctx.lineCap = 'round';
7692    ctx.lineJoin = 'round';
7693    if (currentPath.length >= 2) {
7694      ctx.beginPath();
7695      ctx.moveTo(currentPath[currentPath.length - 2].x, currentPath[currentPath.length - 2].y);
7696      ctx.lineTo(pos.x, pos.y);
7697      ctx.stroke();
7698    }
7699    e.preventDefault();
7700  }
7701
7702  function onPointerUp(e) {
7703    if (!drawing || disabled) return;
7704    drawing = false;
7705    if (currentPath.length > 0) {
7706      paths.push(currentPath);
7707      currentPath = [];
7708    }
7709    try { suppressDisposed(ref.invokeMethodAsync('RadzenSignaturePad.OnStrokeEnd', canvas.toDataURL('image/png'))); } catch (ex) { }
7710    e.preventDefault();
7711  }
7712
7713  canvas.addEventListener('pointerdown', onPointerDown);
7714  canvas.addEventListener('pointermove', onPointerMove);
7715  canvas.addEventListener('pointerup', onPointerUp);
7716  canvas.addEventListener('pointercancel', onPointerUp);
7717  canvas.style.touchAction = 'none';
7718
7719  return {
7720    update: function(newStrokeColor, newStrokeWidth, newDisabled) {
7721      strokeColor = newStrokeColor;
7722      strokeWidth = newStrokeWidth;
7723      disabled = newDisabled;
7724      redraw();
7725    },
7726    clear: function() {
7727      paths = [];
7728      currentPath = [];
7729      ctx.clearRect(0, 0, canvas.width, canvas.height);
7730    },
7731    dispose: function() {
7732      resizeObserver.disconnect();
7733      canvas.removeEventListener('pointerdown', onPointerDown);
7734      canvas.removeEventListener('pointermove', onPointerMove);
7735      canvas.removeEventListener('pointerup', onPointerUp);
7736      canvas.removeEventListener('pointercancel', onPointerUp);
7737    }
7738  };
7739};
7740Radzen.itemListKeydown = function (e) {
7741  var el = e.target;
7742  if (!el || !el.classList) return;
7743  var radio = el.classList.contains('rz-radio-button-list');
7744  var checkbox = el.classList.contains('rz-checkbox-list');
7745  if (!radio && !checkbox) return;
7746  var key = e.code ? e.code : e.key;
7747  if (key === 'ArrowLeft' || key === 'ArrowRight' || key === 'ArrowUp' || key === 'ArrowDown') {
7748    var horizontal = key === 'ArrowLeft' || key === 'ArrowRight';
7749    if (radio || el.classList.contains(horizontal ? 'rz-checkbox-list-horizontal' : 'rz-checkbox-list-vertical')) {
7750      e.preventDefault();
7751    }
7752  } else if (key === 'Home' || key === 'End' || key === 'Space' || key === 'Enter') {
7753    e.preventDefault();
7754  }
7755};
7756document.addEventListener('keydown', Radzen.itemListKeydown);
7757Radzen.chatKeydown = function (e) {
7758  var el = e.target;
7759  if (!el || !el.classList || !el.classList.contains('rz-chat-textarea')) return;
7760  var container = el.closest('.rz-chat-input');
7761  var popupOpen = container && container.querySelector('.rz-chat-mention-popup');
7762  if (e.key === 'Enter') {
7763    if (!e.shiftKey || popupOpen) {
7764      e.preventDefault();
7765    }
7766  } else if (popupOpen && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
7767    e.preventDefault();
7768  } else if ((e.key === 'Backspace' || e.key === 'Delete') && el.dataset.mentionSegments) {
7769    var start = el.selectionStart;
7770    var end = el.selectionEnd;
7771    var segments = el.dataset.mentionSegments.split(',');
7772    for (var i = 0; i < segments.length; i++) {
7773      var parts = segments[i].split(':');
7774      var segmentStart = parseInt(parts[0], 10);
7775      var segmentEnd = segmentStart + parseInt(parts[1], 10);
7776      var hit;
7777      if (start !== end) {
7778        hit = segmentStart < end && segmentEnd > start;
7779      } else {
7780        var target = e.key === 'Backspace' ? start - 1 : start;
7781        hit = target >= 0 && segmentStart <= target && segmentEnd > target;
7782      }
7783      if (hit) {
7784        e.preventDefault();
7785        break;
7786      }
7787    }
7788  }
7789};
7790document.addEventListener('keydown', Radzen.chatKeydown);
7791Radzen.popupTriggerKeydown = function (e) {
7792  if (e.altKey || e.ctrlKey || e.metaKey) return;
7793  var el = e.target;
7794  if (!el || !el.classList) return;
7795  var key = e.code ? e.code : e.key;
7796  var vertical = key === 'ArrowUp' || key === 'ArrowDown';
7797  if (el.classList.contains('rz-splitbutton-menubutton')) {
7798    if (vertical) e.preventDefault();
7799  } else if (el.classList.contains('rz-menu-list') || el.classList.contains('rz-navigation-menu')) {
7800    if (vertical || key === 'Home' || key === 'End' || key === 'Space') e.preventDefault();
7801  } else if (el.classList.contains('rz-navigation-item-wrapper') && el.getAttribute('aria-haspopup')) {
7802    if (vertical || key === 'Home' || key === 'End' || key === 'Space') e.preventDefault();
7803  } else if (el.classList.contains('rz-dropdown') || el.classList.contains('rz-listbox')) {
7804    if (vertical || key === 'ArrowLeft' || key === 'ArrowRight' || key === 'Space') e.preventDefault();
7805  } else if (el.classList.contains('rz-colorpicker')) {
7806    if (key === 'Space') e.preventDefault();
7807  }
7808};
7809document.addEventListener('keydown', Radzen.popupTriggerKeydown);
7810Radzen.datePickerKeydown = function (e) {
7811  if (e.isComposing) return;
7812  var el = e.target;
7813  if (!el || el.tagName !== 'INPUT' || !el.classList || !el.classList.contains('rz-inputtext') || !el.closest('.rz-datepicker')) return;
7814  var key = e.code ? e.code : e.key;
7815  if (key === 'Enter' || (e.altKey && key === 'ArrowDown')) {
7816    e.preventDefault();
7817  }
7818};
7819document.addEventListener('keydown', Radzen.datePickerKeydown);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.