PageSourceSearch

https://samusichalloffame.com/_content/Radzen.Blazor/Radzen.Blazor.js?v=11.4.1.0

js samusichalloffame.com collected 2026-09-25 01:51:26 UTC 288,324 bytes, 7,755 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    var p = parent;
2309    while (p && p != document.body) {
2310        if (p.scrollWidth > p.clientWidth || p.scrollHeight > p.clientHeight) {
2311            p.removeEventListener('scroll', Radzen.closeAllPopups);
2312            p.addEventListener('scroll', Radzen.closeAllPopups);
2313        }
2314        p = p.parentElement;
2315    }
2316
2317    if (!parent) {
2318        document.removeEventListener('contextmenu', Radzen[id]);
2319        document.addEventListener('contextmenu', Radzen[id]);
2320    }
2321
2322    if (autoFocusFirstElement) {
2323        setTimeout(function () {
2324            popup.removeEventListener('keydown', Radzen.focusTrap);
2325            popup.addEventListener('keydown', Radzen.focusTrap);
2326
2327            var focusable = Radzen.getFocusableElements(popup);
2328            var firstFocusable = focusable[0];
2329            if (firstFocusable) {
2330                firstFocusable.focus();
2331            }
2332        }, 200);
2333    }
2334
2335    if (popup.__escapeHandler) {
2336        popup.removeEventListener('keydown', popup.__escapeHandler, true);
2337    }
2338    popup.__escapeHandler = function (e) {
2339        if (e.key === 'Escape' || e.key === 'Esc') {
2340            var menu = popup.querySelector('.rz-menu[role="menu"]');
2341            if (menu && menu.querySelector('.rz-navigation-item-active')) {
2342                return;
2343            }
2344            Radzen.closePopup(id, instance, callback, e);
2345        }
2346        if (e.key === 'Tab' && parent && (popup.classList.contains('rz-dropdown-panel') || popup.classList.contains('rz-multiselect-panel'))) {
2347            e.preventDefault();
2348        }
2349        if (e.key === 'Tab' && !e.shiftKey) {
2350            var timepicker = popup.querySelector('.rz-timepicker');
2351            if (timepicker) {
2352                var focusable = Radzen.getFocusableElements(timepicker);
2353                var last = focusable[focusable.length - 1];
2354                if (last && document.activeElement === last) {
2355                    Radzen.closePopup(id, instance, callback, e);
2356                }
2357            }
2358        }
2359    };
2360    popup.addEventListener('keydown', popup.__escapeHandler, true);
2361  },
2362  closeAllPopups: function (e, id) {
2363    if (!Radzen.popups) return;
2364    var el = e && e.target || id && documentElement.getElementById(id);
2365    var popups = Radzen.popups;
2366      for (var i = 0; i < popups.length; i++) {
2367        var p = popups[i];
2368
2369        var closestPopup = el && el.closest && (el.closest('.rz-popup') || el.closest('.rz-overlaypanel'));
2370        if (closestPopup && closestPopup != p) {
2371            return;
2372        }
2373
2374        Radzen.closePopup(p.id, p.instance, p.callback, e);
2375    }
2376    Radzen.popups = [];
2377  },
2378  closePopup: function (id, instance, callback, e, preventFocusRestore) {
2379    var popup = document.getElementById(id);
2380    if (!popup) return;
2381    var popupInfo = (Radzen.popups || []).find(function (p) { return p.id === id; });
2382    if (popupInfo && popupInfo.parent) {
2383      Radzen.setPopupAriaExpanded(popupInfo.parent, id, false);
2384      var combobox = popupInfo.parent.closest('[role="combobox"]');
2385      if (combobox) {
2386        combobox.removeAttribute('aria-activedescendant');
2387      }
2388    }
2389    if (popup.style.display == 'none') {
2390        var popups = Radzen.findPopup(id);
2391        if (popups.length > 1) {
2392            for (var i = 0; i < popups.length; i++) {
2393                if (popups[i].style.display == 'none') {
2394                    popups[i].parentNode.removeChild(popups[i]);
2395                } else {
2396                    popup = popups[i];
2397                }
2398            }
2399        } else if (popups.length == 1 && popups[0].style.display != 'none') {
2400            popup = popups[0];
2401        } else {
2402            return;
2403        }
2404    }
2405
2406    if (popup) {
2407      if (popup.minWidth) {
2408          popup.style.minWidth = '';
2409      }
2410
2411      if (window.chrome && Radzen[id + 'FZL']) {
2412        Radzen[id + 'FZL'].cell.style.left = Radzen[id + 'FZL'].left;
2413        Radzen[id + 'FZL'] = null;
2414      }
2415
2416      var hidePopup = function () {
2417          popup.style.display = 'none';
2418          popup.onanimationend = null;
2419          popup.onanimationcancel = null;
2420      }
2421      popup.onanimationend = hidePopup;
2422      popup.onanimationcancel = hidePopup;
2423      popup.classList.add("rz-close");
2424      popup.classList.remove("rz-open");
2425      var closeAnimationRunning = popup.getAnimations
2426          ? popup.getAnimations().some(function (a) { return a.animationName == 'rz-close'; })
2427          : getComputedStyle(popup).animationName.indexOf('rz-close') != -1;
2428      if (!closeAnimationRunning) {
2429          hidePopup();
2430      }
2431    }
2432    if (popup && popup.__escapeHandler) {
2433        popup.removeEventListener('keydown', popup.__escapeHandler, true);
2434        delete popup.__escapeHandler;
2435    }
2436    if (popup && popup.__viewportResizeHandler) {
2437        if (window.visualViewport) {
2438            window.visualViewport.removeEventListener('resize', popup.__viewportResizeHandler);
2439        }
2440        var wrapperToReset = popup.querySelector('.rz-dropdown-items-wrapper, .rz-multiselect-items-wrapper');
2441        if (wrapperToReset && popup.__originalWrapperMaxHeight !== undefined) {
2442            wrapperToReset.style.maxHeight = popup.__originalWrapperMaxHeight;
2443        }
2444        delete popup.__viewportResizeHandler;
2445        delete popup.__originalWrapperMaxHeight;
2446        delete popup.__originalPopupTop;
2447    }
2448    document.removeEventListener('mousedown', Radzen[id]);
2449    window.removeEventListener('resize', Radzen[id]);
2450    Radzen[id] = null;
2451
2452    if (instance && callback) {
2453        if (callback.includes('RadzenTooltip')) {
2454            try { suppressDisposed(instance.invokeMethodAsync(callback, null)); } catch { }
2455        } else {
2456            try { suppressDisposed(instance.invokeMethodAsync(callback)); } catch { }
2457        }
2458    }
2459    Radzen.popups = (Radzen.popups || []).filter(function (obj) {
2460        return obj.id !== id;
2461    });
2462
2463    var closedByOutsideMousedown = e && e.type == 'mousedown' && e.target && e.target.nodeType &&
2464        popup && !popup.contains(e.target) &&
2465        !(popupInfo && popupInfo.parent && popupInfo.parent.contains(e.target));
2466
2467    if (!preventFocusRestore && !closedByOutsideMousedown &&
2468        (Radzen.activeElement && Radzen.activeElement == document.activeElement ||
2469        Radzen.activeElement && document.activeElement == document.body ||
2470        Radzen.activeElement && popup && popup.contains(document.activeElement) ||
2471        Radzen.activeElement && document.activeElement &&
2472            (document.activeElement.classList.contains('rz-dropdown-filter') ||
2473             document.activeElement.classList.contains('rz-lookup-search-input') ||
2474             document.activeElement.classList.contains('rz-multiselect-filter-container') ||
2475             document.activeElement.closest('.rz-multiselect-filter-container') !== null))) {
2476        setTimeout(function () {
2477            if (e && e.target && e.target.tabIndex != -1) {
2478                Radzen.activeElement = e.target;
2479            }
2480            if (Radzen.activeElement && (document.activeElement == null || document.activeElement == document.body || popup && popup.contains(document.activeElement))) {
2481               Radzen.activeElement.focus();
2482            }
2483            Radzen.activeElement = null;
2484        }, 100);
2485    } else if (closedByOutsideMousedown) {
2486        Radzen.activeElement = null;
2487    }
2488  },
2489  popupOpened: function (id) {
2490    var popup = document.getElementById(id);
2491    if (popup) {
2492        return popup.style.display != 'none';
2493    }
2494    return false;
2495  },
2496  togglePopup: function (parent, id, syncWidth, instance, callback, closeOnDocumentClick = true, autoFocusFirstElement = false) {
2497    var popup = document.getElementById(id);
2498    if (!popup) return;
2499    if (popup.style.display == 'block') {
2500      Radzen.closePopup(id, instance, callback);
2501    } else {
2502      Radzen.openPopup(parent, id, syncWidth, null, null, null, instance, callback, closeOnDocumentClick, autoFocusFirstElement);
2503    }
2504  },
2505  destroyPopup: function (id) {
2506    var popup = document.getElementById(id);
2507    if (popup) {
2508      if (popup.__escapeHandler) {
2509          popup.removeEventListener('keydown', popup.__escapeHandler, true);
2510          delete popup.__escapeHandler;
2511      }
2512      if (popup.__viewportResizeHandler) {
2513          if (window.visualViewport) {
2514              window.visualViewport.removeEventListener('resize', popup.__viewportResizeHandler);
2515          }
2516          delete popup.__viewportResizeHandler;
2517          delete popup.__originalWrapperMaxHeight;
2518          delete popup.__originalPopupTop;
2519      }
2520      if (popup.__radzenHome && popup.__radzenHome.isConnected) {
2521          popup.__radzenHome.appendChild(popup);
2522      } else if (popup.parentNode === document.body) {
2523          document.body.removeChild(popup);
2524      }
2525      delete popup.__radzenHome;
2526    }
2527    document.removeEventListener('mousedown', Radzen[id]);
2528    document.removeEventListener('contextmenu', Radzen[id]);
2529    window.removeEventListener('resize', Radzen[id]);
2530    Radzen[id] = null;
2531    Radzen.popups = (Radzen.popups || []).filter(function (obj) {
2532        return obj.id !== id;
2533    });
2534  },
2535  scrollDataGrid: function (e) {
2536    var scrollLeft =
2537      (e.target.scrollLeft ? '-' + e.target.scrollLeft : 0) + 'px';
2538
2539      e.target.previousElementSibling.style.marginLeft = scrollLeft;
2540      e.target.previousElementSibling.firstElementChild.style.paddingRight =
2541          e.target.clientHeight < e.target.scrollHeight ? (e.target.offsetWidth - e.target.clientWidth) + 'px' : '0px';
2542
2543    if (e.target.nextElementSibling) {
2544        e.target.nextElementSibling.style.marginLeft = scrollLeft;
2545        e.target.nextElementSibling.firstElementChild.style.paddingRight =
2546            e.target.clientHeight < e.target.scrollHeight ? (e.target.offsetWidth - e.target.clientWidth) + 'px' : '0px';
2547    }
2548
2549    for (var i = 0; i < document.body.children.length; i++) {
2550        if (document.body.children[i].classList.contains('rz-overlaypanel')) {
2551            document.body.children[i].style.display = 'none';
2552        }
2553    }
2554  },
2555  focusFirstFocusableElement: function (el) {
2556      var focusable = Radzen.getFocusableElements(el);
2557      if (!focusable || !focusable.length) return;
2558
2559      var first = focusable[0];
2560      
2561      if (first.classList.contains('rz-html-editor-content')) {
2562          var sel = window.getSelection();
2563          var range = document.createRange();
2564          range.setStart(first, 0);
2565          range.setEnd(first, 0);
2566          sel.removeAllRanges();
2567          sel.addRange(range);
2568          first.focus();
2569      } else {
2570          first.focus();
2571      }
2572  },
2573  openSideDialog: function (options, dialogService) {
2574      if (dialogService) {
2575          Radzen.sideDialogService = dialogService;
2576      }
2577      if (!Radzen.sideDialogInvokerElement) {
2578          var invoker = document.activeElement;
2579          if (invoker && invoker !== document.body && !invoker.closest('.rz-dialog-side')) {
2580              Radzen.sideDialogInvokerElement = invoker;
2581          }
2582      }
2583      var sideDialog = document.querySelector('.rz-dialog-side');
2584      if (sideDialog) {
2585          sideDialog.removeEventListener('keydown', Radzen.focusTrap);
2586          if (options.showMask) {
2587              sideDialog.addEventListener('keydown', Radzen.focusTrap);
2588          }
2589          sideDialog.removeEventListener('keydown', Radzen.closeSideDialogOnEsc);
2590          sideDialog.addEventListener('keydown', Radzen.closeSideDialogOnEsc);
2591      }
2592      setTimeout(function () {
2593          if (options.autoFocusFirstElement) {
2594              var dialogs = document.querySelectorAll('.rz-dialog-side-content');
2595              if (dialogs.length == 0) return;
2596              var lastDialog = dialogs[dialogs.length - 1];
2597              Radzen.focusFirstFocusableElement(lastDialog);
2598          }
2599      }, 500);
2600  },
2601  closeSideDialog: function () {
2602      delete Radzen.sideDialogService;
2603      var invoker = Radzen.sideDialogInvokerElement;
2604      Radzen.sideDialogInvokerElement = null;
2605      if (invoker && typeof invoker.focus === 'function' && document.body.contains(invoker)) {
2606          invoker.focus();
2607      }
2608  },
2609  closeSideDialogOnEsc: function (e) {
2610      e = e || window.event;
2611      var isEscape = "key" in e ? (e.key === "Escape" || e.key === "Esc") : (e.keyCode === 27);
2612      if (isEscape && Radzen.sideDialogService) {
2613          var popups = document.querySelectorAll('.rz-popup,.rz-overlaypanel');
2614          for (var i = 0; i < popups.length; i++) {
2615              if (popups[i].style.display != 'none') {
2616                  return;
2617              }
2618          }
2619          try {
2620              Radzen.sideDialogService.invokeMethodAsync('DialogService.TryCloseSide', null);
2621          } catch (ex) { }
2622      }
2623  },
2624  createSideDialogResizer: function(handle, sideDialog, options) {
2625      const normalizeDir = (value) => {
2626          if (typeof value === 'string' && value.length) {
2627              return value.toLowerCase();
2628          }
2629          if (typeof value === 'number') {
2630              const positions = ['right', 'left', 'top', 'bottom'];
2631              return positions[value] || 'right';
2632          }
2633          return 'right';
2634      };
2635
2636      const dir = normalizeDir(options?.position);
2637
2638      let start = null;
2639
2640      const onDown = (e) => {
2641          e.preventDefault();
2642
2643          start = {x: e.clientX, y: e.clientY, w: sideDialog.clientWidth, h: sideDialog.clientHeight};
2644
2645          document.addEventListener('pointermove', onMove);
2646          document.addEventListener('pointerup', onUp, {once: true});
2647          document.addEventListener('pointercancel', onUp, {once: true});
2648      };
2649
2650      const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
2651
2652      const applyWidth = (w) => {
2653          sideDialog.style.width = Math.round(w) + 'px';
2654      };
2655      const applyHeight = (h) => {
2656          sideDialog.style.height = `${Math.round(h)}px`;
2657      };
2658
2659      const onMove = (e) => {
2660          if (!start) return;
2661
2662          const dx = e.clientX - start.x;
2663          const dy = e.clientY - start.y;
2664
2665          switch (dir) {
2666              case 'right':
2667                  applyWidth(clamp(start.w - dx, options.minWidth, Infinity));
2668                  break;
2669              case 'left':
2670                  applyWidth(clamp(start.w + dx, options.minWidth, Infinity));
2671                  break;
2672              case 'bottom':
2673                  applyHeight(clamp(start.h - dy, options.minHeight, Infinity));
2674                  break;
2675              case 'top':
2676                  applyHeight(clamp(start.h + dy, options.minHeight, Infinity));
2677                  break;
2678          }
2679      };
2680
2681      const onUp = (e) => {
2682          start = null;
2683          document.removeEventListener('pointermove', onMove);
2684      };
2685
2686      handle.addEventListener('pointerdown', onDown);
2687
2688      return {
2689          dispose() {
2690              handle.removeEventListener('pointerdown', onDown);
2691              document.removeEventListener('pointermove', onMove);
2692          }
2693      };
2694  },
2695  openDialog: function (options, dialogService, dialog) {
2696    if (!Radzen.dialogInvokerElement) {
2697        var invoker = document.activeElement;
2698        if (invoker && invoker !== document.body && !invoker.closest('.rz-dialog-content')) {
2699            Radzen.dialogInvokerElement = invoker;
2700        }
2701    }
2702    if (Radzen.closeAllPopups) {
2703        Radzen.closeAllPopups();
2704    }
2705    Radzen.dialogService = dialogService;
2706    if (
2707      document.documentElement.scrollHeight >
2708      document.documentElement.clientHeight
2709    ) {
2710      document.body.classList.add('no-scroll');
2711    }
2712
2713    var currentDialogs = document.querySelectorAll('.rz-dialog-content');
2714    var currentDialog = currentDialogs.length ? currentDialogs[currentDialogs.length - 1] : null;
2715    if (currentDialog) {
2716        currentDialog.options = options;
2717        var dialogRoot = currentDialog.closest('.rz-dialog') || currentDialog;
2718        dialogRoot.removeEventListener('keydown', Radzen.focusTrap);
2719        dialogRoot.addEventListener('keydown', Radzen.focusTrap);
2720    }
2721
2722    setTimeout(function () {
2723        var dialogs = document.querySelectorAll('.rz-dialog-content');
2724        if (dialogs.length == 0) return;
2725        var lastDialog = dialogs[dialogs.length - 1];
2726
2727        if (lastDialog) {
2728            lastDialog.options = options;
2729
2730            if (options.resizable) {
2731                var dialogElement = lastDialog.parentElement;
2732
2733                dialog.offsetWidth = dialogElement.offsetWidth;
2734                dialog.offsetHeight = dialogElement.offsetHeight;
2735
2736                var userResizing = false;
2737
2738                var pushSize = function () {
2739                    if (!dialog) return;
2740                    if (dialog.offsetWidth == dialogElement.offsetWidth &&
2741                        dialog.offsetHeight == dialogElement.offsetHeight) return;
2742
2743                    dialog.offsetWidth = dialogElement.offsetWidth;
2744                    dialog.offsetHeight = dialogElement.offsetHeight;
2745
2746                    dialog.invokeMethodAsync(
2747                        'RadzenDialog.OnResize',
2748                        dialogElement.offsetWidth,
2749                        dialogElement.offsetHeight
2750                    ).catch(function () { });
2751                };
2752
2753                var stopTracking = function () {
2754                    document.removeEventListener('pointermove', onPointerMove, true);
2755                    document.removeEventListener('pointerup', onPointerUp, true);
2756                    document.removeEventListener('pointercancel', onPointerUp, true);
2757                };
2758
2759                var onPointerMove = function (e) {
2760                    if (e.buttons === 1) {
2761                        userResizing = true;
2762                        return;
2763                    }
2764
2765                    onPointerUp();
2766                };
2767
2768                var onPointerUp = function () {
2769                    stopTracking();
2770                    if (!userResizing) return;
2771                    userResizing = false;
2772                    pushSize();
2773                };
2774
2775                var onPointerDown = function (e) {
2776                    if (e.button !== 0 || !e.isPrimary || e.target !== dialogElement) return;
2777
2778                    document.addEventListener('pointermove', onPointerMove, true);
2779                    document.addEventListener('pointerup', onPointerUp, true);
2780                    document.addEventListener('pointercancel', onPointerUp, true);
2781                };
2782
2783                var dialogResize = function (e) {
2784                    if (!dialog) return;
2785
2786                    if (!e[0].target.isConnected) {
2787                        resizer.dispose();
2788                        return;
2789                    }
2790
2791                    if (!userResizing) {
2792                        dialog.offsetWidth = e[0].target.offsetWidth;
2793                        dialog.offsetHeight = e[0].target.offsetHeight;
2794                        return;
2795                    }
2796
2797                    pushSize();
2798                };
2799
2800                var resizeObserver = new ResizeObserver(dialogResize);
2801                resizeObserver.observe(dialogElement);
2802
2803                dialogElement.addEventListener('pointerdown', onPointerDown);
2804
2805                var resizer = {
2806                    disposed: false,
2807                    dispose: function () {
2808                        if (this.disposed) return;
2809                        this.disposed = true;
2810                        resizeObserver.disconnect();
2811                        dialogElement.removeEventListener('pointerdown', onPointerDown);
2812                        stopTracking();
2813                        var index = Radzen.dialogResizers.indexOf(this);
2814                        if (index != -1) {
2815                            Radzen.dialogResizers.splice(index, 1);
2816                        }
2817                        if (Radzen.dialogResizer === this) {
2818                            Radzen.dialogResizer = null;
2819                        }
2820                    },
2821                    disconnect: function () { this.dispose(); }
2822                };
2823
2824                Radzen.dialogResizers = Radzen.dialogResizers || [];
2825                Radzen.dialogResizers.push(resizer);
2826                Radzen.dialogResizer = resizer;
2827            }
2828
2829            if (options.draggable) {
2830                var dialogTitle = lastDialog.parentElement.querySelector('.rz-dialog-titlebar');
2831                if (dialogTitle) {
2832                    dialogTitle.dragHandler = function (e) {
2833                        var rect = lastDialog.parentElement.getBoundingClientRect();
2834                        var offsetX = e.clientX - rect.left;
2835                        var offsetY = e.clientY - rect.top;
2836
2837                        var move = function (e) {
2838                            var left = e.clientX - offsetX;
2839                            var top = e.clientY - offsetY;
2840
2841                            lastDialog.parentElement.style.left = left + 'px';
2842                            lastDialog.parentElement.style.top = top + 'px';
2843
2844                            try { suppressDisposed(dialog.invokeMethodAsync('RadzenDialog.OnDrag', top, left)); } catch { }
2845                        };
2846
2847                        var stop = function () {
2848                            document.removeEventListener('mousemove', move);
2849                            document.removeEventListener('mouseup', stop);
2850                        };
2851
2852                        document.addEventListener('mousemove', move);
2853                        document.addEventListener('mouseup', stop);
2854                    };
2855
2856                    dialogTitle.addEventListener('mousedown', dialogTitle.dragHandler);
2857                }
2858            }
2859
2860            if (options.autoFocusFirstElement) {
2861                Radzen.focusFirstFocusableElement(lastDialog);
2862            }
2863        }
2864    }, 500);
2865
2866    document.removeEventListener('keydown', Radzen.closePopupOrDialog);
2867    if (options.closeDialogOnEsc) {
2868        document.addEventListener('keydown', Radzen.closePopupOrDialog);
2869    }
2870  },
2871  closeDialog: function () {
2872    var resizers = (Radzen.dialogResizers || []).slice();
2873    for (var i = 0; i < resizers.length; i++) {
2874        resizers[i].dispose();
2875    }
2876    Radzen.dialogResizers = [];
2877    Radzen.dialogResizer = null;
2878    document.body.classList.remove('no-scroll');
2879    var dialogs = document.querySelectorAll('.rz-dialog-content');
2880
2881    var lastDialog = dialogs.length && dialogs[dialogs.length - 1];
2882    if (lastDialog) {
2883        var dialogTitle = lastDialog.parentElement.querySelector('.rz-dialog-titlebar');
2884        if (dialogTitle && dialogTitle.dragHandler) {
2885            dialogTitle.removeEventListener('mousedown', dialogTitle.dragHandler);
2886            dialogTitle.dragHandler = null;
2887        }
2888    }
2889
2890    if (dialogs.length <= 1) {
2891        document.removeEventListener('keydown', Radzen.closePopupOrDialog);
2892        delete Radzen.dialogService;
2893        Radzen.restoreDialogInvokerFocus();
2894    }
2895  },
2896  restoreDialogInvokerFocus: function () {
2897    var invoker = Radzen.dialogInvokerElement;
2898    Radzen.dialogInvokerElement = null;
2899    if (invoker && typeof invoker.focus === 'function' && document.body.contains(invoker)) {
2900        invoker.focus();
2901    }
2902  },
2903  disableKeydown: function (e) {
2904      e = e || window.event;
2905      e.preventDefault();
2906  },
2907  getFocusableElements: function (element) {
2908    return [...element.querySelectorAll('a, button, input, textarea, select, details, iframe, embed, object, summary, dialog, audio[controls], video[controls], [contenteditable], [tabindex]')]
2909      .filter(el => {
2910        if (!el || el.hasAttribute('disabled') || el.offsetParent === null) return false;
2911    
2912        // If this is inside a .rz-html-editor with tabindex="-1", skip it
2913        var editorParent = el.closest('.rz-html-editor');
2914        if (editorParent && editorParent.hasAttribute('tabindex') && editorParent.tabIndex === -1) return false;
2915        else if (editorParent) return true;
2916    
2917        return el.tabIndex > -1;
2918    });
2919  },
2920  focusTrap: function (e) {
2921    e = e || window.event;
2922    var isTab = false;
2923    if ("key" in e) {
2924        isTab = (e.key === "Tab");
2925    } else {
2926        isTab = (e.keyCode === 9);
2927    }
2928    if (isTab) {
2929        var focusable = Radzen.getFocusableElements(e.currentTarget);
2930        var firstFocusable = focusable[0];
2931        var lastFocusable = focusable[focusable.length - 1];
2932
2933        if (firstFocusable && lastFocusable && e.shiftKey && document.activeElement === firstFocusable) {
2934            e.preventDefault();
2935            lastFocusable.focus();
2936        } else if (firstFocusable && lastFocusable && !e.shiftKey && document.activeElement === lastFocusable) {
2937            e.preventDefault();
2938            firstFocusable.focus();
2939        }
2940    }
2941  },
2942  closePopupOrDialog: function (e) {
2943      e = e || window.event;
2944      var isEscape = false;
2945      if ("key" in e) {
2946          isEscape = (e.key === "Escape" || e.key === "Esc");
2947      } else {
2948          isEscape = (e.keyCode === 27);
2949      }
2950      if (isEscape && Radzen.dialogService) {
2951          var popups = document.querySelectorAll('.rz-popup,.rz-overlaypanel');
2952          for (var i = 0; i < popups.length; i++) {
2953              if (popups[i].style.display != 'none') {
2954                  return;
2955              }
2956          }
2957
2958          var dialogs = document.querySelectorAll('.rz-dialog-content');
2959          if (dialogs.length == 0) return;
2960          var lastDialog = dialogs[dialogs.length - 1];
2961
2962          if (lastDialog && lastDialog.options && lastDialog.options.closeDialogOnEsc) {
2963              try {
2964                  Radzen.dialogService.invokeMethodAsync('DialogService.TryClose', null).then(function(closed) {
2965                      if (closed && dialogs.length <= 1) {
2966                          document.removeEventListener('keydown', Radzen.closePopupOrDialog);
2967                          delete Radzen.dialogService;
2968                          var layout = document.querySelector('.rz-layout');
2969                          if (layout) {
2970                              layout.removeEventListener('keydown', Radzen.disableKeydown);
2971                          }
2972                      }
2973                  });
2974              } catch (e) { }
2975          }
2976      }
2977  },
2978  getNumericValue: function (arg) {
2979    var el =
2980      arg instanceof Element || arg instanceof HTMLDocument
2981        ? arg
2982        : document.getElementById(arg);
2983    return el ? Radzen.getInputValue(el.children[0]) : null;
2984  },
2985  getInputValue: function (arg) {
2986    var input =
2987      arg instanceof Element || arg instanceof HTMLDocument
2988        ? arg
2989        : document.getElementById(arg);
2990    return input && input.value != '' ? input.value : null;
2991  },
2992  setInputValue: function (arg, value) {
2993    var input =
2994      arg instanceof Element || arg instanceof HTMLDocument
2995        ? arg
2996        : document.getElementById(arg);
2997    if (input) {
2998      input.value = value;
2999    }
3000  },
3001  getSelectionRange: function (arg) {
3002    var input =
3003      arg instanceof Element || arg instanceof HTMLDocument
3004        ? arg
3005        : document.getElementById(arg);
3006    if (!input) return null;
3007    try {
3008      if (input.selectionStart === null || input.selectionStart === undefined) return null;
3009      return [input.selectionStart, input.selectionEnd];
3010    } catch (e) {
3011      return null;
3012    }
3013  },
3014  setSelectionRange: function (arg, start, end) {
3015    var input =
3016      arg instanceof Element || arg instanceof HTMLDocument
3017        ? arg
3018        : document.getElementById(arg);
3019    if (!input) return;
3020    var max = input.value ? input.value.length : 0;
3021    var s = Math.max(0, Math.min(start, max));
3022    var e = Math.max(0, Math.min(end, max));
3023    try {
3024      if (typeof input.setSelectionRange === 'function') {
3025        input.setSelectionRange(s, e);
3026      } else {
3027        input.selectionStart = s;
3028        input.selectionEnd = e;
3029      }
3030    } catch (ex) { }
3031  },
3032  isScrolledToBottom: function (arg, threshold) {
3033    var element =
3034      arg instanceof Element || arg instanceof HTMLDocument
3035        ? arg
3036        : document.getElementById(arg);
3037    if (!element) return false;
3038    var offset = typeof threshold === 'number' ? threshold : 0;
3039    return element.scrollTop + element.clientHeight >= element.scrollHeight - offset;
3040  },
3041  blur: function (el, e) { 
3042    if (el) {
3043        e.preventDefault();
3044        el.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, keyCode: 9 }));
3045    }
3046  },
3047  readFileAsBase64: function (fileInput, maxFileSize, maxWidth, maxHeight) {
3048    var calculateWidthAndHeight = function (img) {
3049      var width = img.width;
3050      var height = img.height;
3051      // Change the resizing logic
3052      if (width > height) {
3053        if (width > maxWidth) {
3054          height = height * (maxWidth / width);
3055          width = maxWidth;
3056        }
3057      } else {
3058        if (height > maxHeight) {
3059          width = width * (maxHeight / height);
3060          height = maxHeight;
3061        }
3062      }
3063      return { width, height };
3064    };
3065    var readAsDataURL = function (fileInput) {
3066      return new Promise(function (resolve, reject) {
3067        var reader = new FileReader();
3068        reader.onerror = function () {
3069          reader.abort();
3070          reject('Error reading file.');
3071        };
3072        reader.addEventListener(
3073          'load',
3074          function () {
3075            if (fileInput.files[0] && fileInput.files[0].type.includes('image') && maxWidth > 0 && maxHeight > 0) {
3076              var img = document.createElement("img");
3077              img.onload = function (event) {
3078                // Dynamically create a canvas element
3079                var canvas = document.createElement("canvas");
3080                var res = calculateWidthAndHeight(img);
3081                canvas.width = res.width;
3082                canvas.height = res.height;
3083                var ctx = canvas.getContext("2d");
3084                ctx.drawImage(img, 0, 0, res.width, res.height);
3085                resolve(canvas.toDataURL(fileInput.type));
3086              }
3087              img.src = reader.result;
3088            } else {
3089              resolve(reader.result);
3090            }
3091          },
3092          false
3093          );
3094        var file = fileInput.files[0];
3095        if (!file) return;
3096        if (file.size <= maxFileSize) {
3097          reader.readAsDataURL(file);
3098        } else {
3099          reject('File too large.');
3100        }
3101      });
3102    };
3103
3104    return readAsDataURL(fileInput);
3105  },
3106  closeMenuItem: function (item) {
3107    if (!item || !item.classList || !item.classList.contains('rz-navigation-item-active')) {
3108      return;
3109    }
3110
3111    item.classList.remove('rz-navigation-item-active');
3112
3113    var wrapper = item.querySelector('.rz-navigation-item-wrapper');
3114    if (wrapper) {
3115      wrapper.classList.remove('rz-navigation-item-wrapper-active');
3116    }
3117
3118    var children = item.querySelector('.rz-navigation-menu');
3119    if (children) {
3120      item.setAttribute('aria-expanded', false);
3121      children.onanimationend = function () {
3122        children.style.display = 'none';
3123        children.style.insetInlineStart = '';
3124        children.onanimationend = null;
3125      };
3126      children.classList.remove('rz-open');
3127      children.classList.add('rz-close');
3128
3129      children.querySelectorAll('.rz-navigation-item-active').forEach(function (nested) {
3130        Radzen.closeMenuItem(nested);
3131      });
3132    }
3133
3134    var icon = item.querySelector('.rz-navigation-item-icon-children');
3135    if (icon) {
3136      icon.classList.remove('rz-state-expanded');
3137      icon.classList.add('rz-state-collapsed');
3138    }
3139  },
3140  toggleMenuItem: function (target, event, defaultActive, clickToOpen) {
3141    if (!target) return;
3142
3143    var item = target.closest('.rz-navigation-item');
3144
3145    if (!item) return;
3146
3147    var active = defaultActive != undefined ? defaultActive : !item.classList.contains('rz-navigation-item-active');
3148
3149    function toggle(active) {
3150      if (active && item.parentElement) {
3151        Array.prototype.forEach.call(item.parentElement.children, function (sibling) {
3152          if (sibling !== item) {
3153            Radzen.closeMenuItem(sibling);
3154          }
3155        });
3156      }
3157
3158      item.classList.toggle('rz-navigation-item-active', active);
3159
3160      target.classList.toggle('rz-navigation-item-wrapper-active', active);
3161
3162      var children = item.querySelector('.rz-navigation-menu');
3163
3164      if (children) {
3165        item.setAttribute('aria-expanded', active);
3166        if (active) {
3167          children.onanimationend = null;
3168          children.style.display = '';
3169          children.classList.add('rz-open');
3170          children.classList.remove('rz-close');
3171
3172          var isRtl = getComputedStyle(item).direction === 'rtl';
3173          var round = function (value) { return Math.round(value * 1000) / 1000; };
3174          var el = item.parentElement;
3175          var scrollParent = null;
3176          while (el && el !== document.body) {
3177            var s = getComputedStyle(el);
3178            if (s.overflowX === 'auto' || s.overflowX === 'scroll' || s.overflowX === 'hidden') {
3179              scrollParent = el;
3180              break;
3181            }
3182            el = el.parentElement;
3183          }
3184
3185          if (children.hasAttribute('data-flyout')) {
3186            children.removeAttribute('data-flyout-flip');
3187            var childRect = children.getBoundingClientRect();
3188            var itemRect = item.getBoundingClientRect();
3189            if (isRtl) {
3190              var leftBoundary = scrollParent ? scrollParent.getBoundingClientRect().left : 0;
3191              if (round(itemRect.left - childRect.width) < round(leftBoundary)) {
3192                children.setAttribute('data-flyout-flip', '');
3193              }
3194            } else {
3195              var rightBoundary = scrollParent ? scrollParent.getBoundingClientRect().right : document.documentElement.clientWidth;
3196              if (round(itemRect.right + childRect.width) > round(rightBoundary)) {
3197                children.setAttribute('data-flyout-flip', '');
3198              }
3199            }
3200          }
3201
3202          // Shift dropdown left/right if it overflows the scroll parent or viewport
3203          children.style.insetInlineStart = children.style.insetInlineStart || '';
3204          var childRect = children.getBoundingClientRect();
3205          var rightBoundary = scrollParent ? scrollParent.getBoundingClientRect().right : document.documentElement.clientWidth;
3206          var leftBoundary = scrollParent ? scrollParent.getBoundingClientRect().left : 0;
3207          if (!isRtl && round(childRect.right) > round(rightBoundary)) {
3208            var offset = parseFloat(children.style.insetInlineStart) || 0;
3209            children.style.insetInlineStart = (offset + rightBoundary - childRect.right) + 'px';
3210          } else if (isRtl && round(childRect.left) < round(leftBoundary)) {
3211            var offset = parseFloat(children.style.insetInlineStart) || 0;
3212            children.style.insetInlineStart = (offset + leftBoundary - childRect.left) + 'px';
3213          }
3214        } else {
3215          children.onanimationend = function () {
3216            children.style.display = 'none';
3217            children.style.insetInlineStart = '';
3218            children.onanimationend = null;
3219          }
3220          children.classList.remove('rz-open');
3221          children.classList.add('rz-close');
3222        }
3223      }
3224
3225      var icon = item.querySelector('.rz-navigation-item-icon-children');
3226
3227      if (icon) {
3228        icon.classList.toggle('rz-state-expanded', active);
3229        icon.classList.toggle('rz-state-collapsed', !active);
3230      }
3231    }
3232
3233    if (clickToOpen === false && item.parentElement && item.parentElement.closest('.rz-navigation-item') && !defaultActive) {
3234        return;
3235    };
3236
3237    toggle(active);
3238
3239    document.removeEventListener('click', target.clickHandler);
3240
3241    target.clickHandler = function (event) {
3242      if (item.contains(event.target)) {
3243        var child = event.target.closest('.rz-navigation-item');
3244        if (child && child.querySelector('.rz-navigation-menu')) {
3245          return;
3246        }
3247      }
3248      toggle(false);
3249    }
3250
3251    document.addEventListener('click', target.clickHandler);
3252  },
3253  destroyChart: function (ref) {
3254    if(!ref) return;
3255    ref.removeEventListener('mouseleave', ref.mouseLeaveHandler);
3256    delete ref.mouseLeaveHandler;
3257    ref.removeEventListener('mouseenter', ref.mouseEnterHandler);
3258    delete ref.mouseEnterHandler;
3259    ref.removeEventListener('mousemove', ref.mouseMoveHandler);
3260    delete ref.mouseMoveHandler;
3261    ref.removeEventListener('click', ref.clickHandler);
3262    delete ref.clickHandler;
3263    ref.removeEventListener('wheel', ref.wheelHandler);
3264    delete ref.wheelHandler;
3265    if (ref.legendEnterHandler) {
3266      ref.removeEventListener('mouseenter', ref.legendEnterHandler, true);
3267      delete ref.legendEnterHandler;
3268    }
3269    if (ref.legendLeaveHandler) {
3270      ref.removeEventListener('mouseleave', ref.legendLeaveHandler, true);
3271      delete ref.legendLeaveHandler;
3272    }
3273    if (ref.scrollbarMoveHandler) {
3274      document.removeEventListener('mousemove', ref.scrollbarMoveHandler);
3275      delete ref.scrollbarMoveHandler;
3276    }
3277    if (ref.scrollbarUpHandler) {
3278      document.removeEventListener('mouseup', ref.scrollbarUpHandler);
3279      delete ref.scrollbarUpHandler;
3280    }
3281    if (ref.scrollbarTouchMoveHandler) {
3282      document.removeEventListener('touchmove', ref.scrollbarTouchMoveHandler);
3283      delete ref.scrollbarTouchMoveHandler;
3284    }
3285    if (ref.scrollbarTouchEndHandler) {
3286      document.removeEventListener('touchend', ref.scrollbarTouchEndHandler);
3287      delete ref.scrollbarTouchEndHandler;
3288    }
3289    if (ref.touchStartHandler) {
3290      ref.removeEventListener('touchstart', ref.touchStartHandler);
3291      delete ref.touchStartHandler;
3292    }
3293    if (ref.touchMoveHandler) {
3294      ref.removeEventListener('touchmove', ref.touchMoveHandler);
3295      delete ref.touchMoveHandler;
3296    }
3297    if (ref.touchEndHandler) {
3298      ref.removeEventListener('touchend', ref.touchEndHandler);
3299      delete ref.touchEndHandler;
3300    }
3301    if (ref._chartRTLObserver) {
3302      ref._chartRTLObserver.disconnect();
3303      delete ref._chartRTLObserver;
3304    }
3305    this.destroyResizable(ref);
3306  },
3307  createRangeNavigator: function (ref, instance) {
3308    if (!ref) return [0, 0];
3309
3310    var width = ref.offsetWidth;
3311    var height = ref.offsetHeight;
3312
3313    var dragging = null;
3314    var dragStartX = 0;
3315    var dragStartStart = 0;
3316    var dragStartEnd = 0;
3317    var currentStart = 0;
3318    var currentEnd = 1;
3319    var minRange = 0.01;
3320    function isRTL() {
3321      return document.documentElement.dir === 'rtl';
3322    }
3323
3324    function getPositionFromEvent(e) {
3325      var rect = ref.getBoundingClientRect();
3326      var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3327      if (isRTL()) x = rect.width - x;
3328      return Math.max(0, Math.min(1, x / rect.width));
3329    }
3330
3331    function readPositionFromDOM() {
3332      var w = ref.querySelector('.rz-range-nav-window');
3333      if (w) {
3334        var wRect = w.getBoundingClientRect();
3335        var rRect = ref.getBoundingClientRect();
3336        if (rRect.width > 0) {
3337          if (isRTL()) {
3338            currentStart = (rRect.right - wRect.right) / rRect.width;
3339            currentEnd = (rRect.right - wRect.left) / rRect.width;
3340          } else {
3341            currentStart = (wRect.left - rRect.left) / rRect.width;
3342            currentEnd = (wRect.right - rRect.left) / rRect.width;
3343          }
3344        }
3345      }
3346    }
3347
3348    var snapThreshold = 0.01;
3349    function snap(v) { return v < snapThreshold ? 0 : v > 1 - snapThreshold ? 1 : v; }
3350
3351    // Label state — set by Radzen.updateRangeNavigatorLabels
3352    ref.navLabelIsDate = false;
3353    ref.navLabelInputStart = 0;
3354    ref.navLabelInputEnd = 0;
3355
3356    function formatLabel(fraction) {
3357        var inputStart = ref.navLabelInputStart;
3358        var inputEnd = ref.navLabelInputEnd;
3359        if (inputStart === inputEnd) return '';
3360
3361        var value = inputStart + fraction * (inputEnd - inputStart);
3362
3363        if (ref.navLabelIsDate) {
3364            // .NET ticks -> JavaScript Date
3365            var ticksToMs = value / 10000 - 62135596800000;
3366            var d = new Date(ticksToMs);
3367
3368            var format = ref.handleLabelFormatString || "{0:MM/dd/yyyy}";
3369
3370            // Extract the format from "{0:MM/dd/yyyy}"
3371            format = format.replace(/^\{0:/, '').replace(/\}$/, '');
3372
3373            var month = d.getMonth();
3374            var monthNumber = month + 1;
3375            var day = d.getDate();
3376            var hours24 = d.getHours();
3377            var hours12 = hours24 % 12 || 12;
3378
3379            var monthNames = [
3380                'January', 'February', 'March', 'April', 'May', 'June',
3381                'July', 'August', 'September', 'October', 'November', 'December'
3382            ];
3383
3384            var monthNamesShort = [
3385                'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
3386                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
3387            ];
3388
3389            var year = d.getFullYear();
3390
3391            // Single-pass tokenizer. Quoted literals are preserved
3392            return format.replace(
3393                /'([^']*)'|yyyy|yy|MMMM|MMM|MM|M|dd|d|HH|hh|mm|ss|tt/g,
3394                function (match, literal) {
3395                    if (literal !== undefined) {
3396                        return literal;
3397                    }
3398
3399                    switch (match) {
3400                        case 'yyyy':
3401                            return String(year);
3402
3403                        case 'yy':
3404                            return String(year % 100).padStart(2, '0');
3405
3406                        case 'MMMM':
3407                            return monthNames[month];
3408
3409                        case 'MMM':
3410                            return monthNamesShort[month];
3411
3412                        case 'MM':
3413                            return String(monthNumber).padStart(2, '0');
3414
3415                        case 'M':
3416                            return String(monthNumber);
3417
3418                        case 'dd':
3419                            return String(day).padStart(2, '0');
3420
3421                        case 'd':
3422                            return String(day);
3423
3424                        case 'HH':
3425                            return String(hours24).padStart(2, '0');
3426
3427                        case 'hh':
3428                            return String(hours12).padStart(2, '0');
3429
3430                        case 'mm':
3431                            return String(d.getMinutes()).padStart(2, '0');
3432
3433                        case 'ss':
3434                            return String(d.getSeconds()).padStart(2, '0');
3435
3436                        case 'tt':
3437                            return hours24 < 12 ? 'AM' : 'PM';
3438
3439                        default:
3440                            return match;
3441                    }
3442                }
3443            );
3444        }
3445
3446        return Math.round(value).toString();
3447    }
3448
3449    function setLabelText(el, text) {
3450      if (el.firstChild && el.firstChild.nodeType === 3) {
3451        el.firstChild.nodeValue = text;
3452      } else {
3453        el.textContent = text;
3454      }
3455    }
3456
3457    function updateLabels(start, end) {
3458      if (!ref.navLabelInputEnd) return;
3459      var labels = ref.querySelectorAll('.rz-range-nav-label');
3460      if (labels.length >= 2) {
3461        setLabelText(labels[0], formatLabel(start));
3462        setLabelText(labels[1], formatLabel(end));
3463        // Flip label direction when near edges
3464        labels[0].className = 'rz-range-nav-label ' + (start < 0.1 ? 'rz-range-nav-label-end' : 'rz-range-nav-label-start');
3465        labels[1].className = 'rz-range-nav-label ' + (end > 0.9 ? 'rz-range-nav-label-start' : 'rz-range-nav-label-end');
3466      }
3467    }
3468
3469    function updateDOM(start, end) {
3470      currentStart = snap(start);
3471      currentEnd = snap(end);
3472      start = currentStart;
3473      end = currentEnd;
3474      var win = ref.querySelector('.rz-range-nav-window');
3475      var shadeLeft = ref.querySelector('.rz-range-nav-shade-start');
3476      var shadeRight = ref.querySelector('.rz-range-nav-shade-end');
3477      if (win) {
3478        if (isRTL()) {
3479          win.style.right = (start * 100) + '%';
3480          win.style.left = 'auto';
3481        } else {
3482          win.style.left = (start * 100) + '%';
3483          win.style.right = 'auto';
3484        }
3485        win.style.width = ((end - start) * 100) + '%';
3486      }
3487      if (shadeLeft) shadeLeft.style.width = (start * 100) + '%';
3488      if (shadeRight) shadeRight.style.width = ((1 - end) * 100) + '%';
3489      updateLabels(start, end);
3490    }
3491
3492    ref.navTooltipPoints = [];
3493
3494    ref.navHideTooltip = function () {
3495      var tooltip = ref.querySelector('.rz-range-nav-tooltip');
3496      if (tooltip) tooltip.style.display = 'none';
3497    };
3498
3499    function nearestPoint(pos) {
3500      var points = ref.navTooltipPoints;
3501      var lo = 0, hi = points.length - 1;
3502      while (lo < hi) {
3503        var mid = (lo + hi) >> 1;
3504        if (points[mid].x < pos) {
3505          lo = mid + 1;
3506        } else {
3507          hi = mid;
3508        }
3509      }
3510      if (lo > 0 && pos - points[lo - 1].x < points[lo].x - pos) lo--;
3511      return points[lo];
3512    }
3513
3514    function isOverTrack(e) {
3515      var rect = ref.getBoundingClientRect();
3516      return e.clientY <= rect.bottom - (parseFloat(getComputedStyle(ref).paddingBottom) || 0);
3517    }
3518
3519    function clipBounds() {
3520      var bounds = { top: 0, left: 0, right: document.documentElement.clientWidth };
3521      for (var el = ref.parentElement; el && el !== document.body; el = el.parentElement) {
3522        var style = getComputedStyle(el);
3523        var rect = el.getBoundingClientRect();
3524        if (style.overflowY !== 'visible') {
3525          bounds.top = Math.max(bounds.top, rect.top);
3526        }
3527        if (style.overflowX !== 'visible') {
3528          bounds.left = Math.max(bounds.left, rect.left);
3529          bounds.right = Math.min(bounds.right, rect.right);
3530        }
3531        if (style.position === 'fixed') {
3532          break;
3533        }
3534      }
3535      return bounds;
3536    }
3537
3538    function positionTooltip(tooltip, point) {
3539      var track = ref.querySelector('svg');
3540      tooltip.style.insetInlineStart = (point.x * 100) + '%';
3541      tooltip.style.insetBlockStart = (point.y * (track ? track.clientHeight : 0)) + 'px';
3542
3543      var content = tooltip.querySelector('.rz-chart-tooltip-content');
3544      var popup = content.parentElement;
3545      var bounds = clipBounds();
3546      popup.style.left = '';
3547      popup.classList.remove('rz-bottom-chart-tooltip');
3548      popup.classList.add('rz-top-chart-tooltip');
3549      if (content.getBoundingClientRect().top < bounds.top) {
3550        popup.classList.remove('rz-top-chart-tooltip');
3551        popup.classList.add('rz-bottom-chart-tooltip');
3552      }
3553
3554      var rect = popup.getBoundingClientRect();
3555      if (rect.left < bounds.left) {
3556        popup.style.left = (bounds.left - rect.left) + 'px';
3557      } else if (rect.right > bounds.right) {
3558        popup.style.left = (bounds.right - rect.right) + 'px';
3559      }
3560    }
3561
3562    var tooltipPoint = null;
3563
3564    ref.navHover = function (e) {
3565      if (!ref.navTooltipPoints.length) return;
3566
3567      var tooltip = ref.querySelector('.rz-range-nav-tooltip');
3568      if (!tooltip || dragging || !isOverTrack(e)) {
3569        ref.navHideTooltip();
3570        return;
3571      }
3572      var point = nearestPoint(getPositionFromEvent(e));
3573      var wasHidden = tooltip.style.display === 'none';
3574      var content = tooltip.querySelector('.rz-chart-tooltip-content');
3575
3576      if (point !== tooltipPoint || wasHidden) {
3577        tooltipPoint = point;
3578        tooltip.querySelector('.rz-chart-tooltip-title').textContent = point.category;
3579        tooltip.querySelector('.rz-chart-tooltip-item-value').textContent = point.value;
3580        tooltip.style.setProperty('--rz-series-color', point.color || 'currentColor');
3581        content.style.border = '1px solid ' + getComputedStyle(tooltip.querySelector('.rz-active-point-dot')).fill;
3582      }
3583
3584      tooltip.style.display = '';
3585      positionTooltip(tooltip, point);
3586
3587      if (wasHidden) {
3588        content.classList.remove('rz-chart-tooltip-enter');
3589        void content.offsetWidth;
3590        content.classList.add('rz-chart-tooltip-enter');
3591      }
3592    };
3593
3594    function notifyBlazor(start, end) {
3595      instance.invokeMethodAsync('OnNavigatorDrag', snap(start), snap(end)).catch(function (ex) {
3596        console.error('RangeNav invoke error:', ex);
3597      });
3598    }
3599
3600    readPositionFromDOM();
3601
3602    ref.navMouseDown = function (e) {
3603      ref.navHideTooltip();
3604      // Always read fresh position from DOM on mousedown
3605      readPositionFromDOM();
3606
3607      var pos = getPositionFromEvent(e);
3608      var handleZone = 12 / (ref.offsetWidth || 1);
3609      var winRange = currentEnd - currentStart;
3610
3611      if (pos >= currentStart - handleZone && pos <= currentEnd + handleZone) {
3612        var distToLeft = Math.abs(pos - currentStart);
3613        var distToRight = Math.abs(pos - currentEnd);
3614
3615        if (winRange < handleZone * 3) {
3616          dragging = distToLeft <= distToRight ? 'left' : 'right';
3617        } else if (distToLeft <= handleZone) {
3618          dragging = 'left';
3619        } else if (distToRight <= handleZone) {
3620          dragging = 'right';
3621        } else {
3622          dragging = 'pan';
3623        }
3624      } else {
3625        // Click on shade: center selection at click position
3626        var halfRange = winRange / 2;
3627        var newStart = Math.max(0, Math.min(1 - winRange, pos - halfRange));
3628        var newEnd = newStart + winRange;
3629        updateDOM(newStart, newEnd);
3630        notifyBlazor(newStart, newEnd);
3631        return;
3632      }
3633
3634      if (dragging) {
3635        e.preventDefault();
3636        dragStartX = (e.touches ? e.touches[0].clientX : e.clientX);
3637        dragStartStart = currentStart;
3638        dragStartEnd = currentEnd;
3639      }
3640    };
3641
3642    ref.navMouseMove = function (e) {
3643      if (!dragging) return;
3644      e.preventDefault();
3645      var clientX = e.touches ? e.touches[0].clientX : e.clientX;
3646      var dx = clientX - dragStartX;
3647      if (isRTL()) dx = -dx;
3648      var refWidth = ref.offsetWidth;
3649      if (refWidth <= 0) return;
3650      var delta = dx / refWidth;
3651
3652      var newStart = dragStartStart;
3653      var newEnd = dragStartEnd;
3654
3655      if (dragging === 'left') {
3656        newStart = Math.max(0, Math.min(dragStartEnd - minRange, dragStartStart + delta));
3657      } else if (dragging === 'right') {
3658        newEnd = Math.max(dragStartStart + minRange, Math.min(1, dragStartEnd + delta));
3659      } else if (dragging === 'pan') {
3660        var range = dragStartEnd - dragStartStart;
3661        newStart = dragStartStart + delta;
3662        newEnd = newStart + range;
3663        if (newStart < 0) { newStart = 0; newEnd = range; }
3664        if (newEnd > 1) { newEnd = 1; newStart = 1 - range; }
3665      }
3666
3667      updateDOM(newStart, newEnd);
3668    };
3669
3670    ref.navMouseUp = function () {
3671      if (dragging) {
3672        notifyBlazor(currentStart, currentEnd);
3673      }
3674      dragging = null;
3675    };
3676
3677    ref.addEventListener('mousedown', ref.navMouseDown);
3678    ref.addEventListener('touchstart', ref.navMouseDown, { passive: false });
3679    ref.addEventListener('mousemove', ref.navHover);
3680    ref.addEventListener('mouseleave', ref.navHideTooltip);
3681    document.addEventListener('mousemove', ref.navMouseMove);
3682    document.addEventListener('touchmove', ref.navMouseMove, { passive: false });
3683    document.addEventListener('mouseup', ref.navMouseUp);
3684    document.addEventListener('touchend', ref.navMouseUp);
3685
3686    ref.navResizeObserver = new ResizeObserver(function (entries) {
3687      for (var entry of entries) {
3688        var w = entry.contentRect.width;
3689        var h = entry.contentRect.height;
3690        if (w > 0) {
3691          try { suppressDisposed(instance.invokeMethodAsync('OnResize', w, h)); } catch (ex) {}
3692        }
3693      }
3694    });
3695    ref.navResizeObserver.observe(ref);
3696
3697    ref._radzenDispose = function () {
3698      if (ref.navMouseDown) {
3699        ref.removeEventListener('mousedown', ref.navMouseDown);
3700        ref.removeEventListener('touchstart', ref.navMouseDown);
3701        delete ref.navMouseDown;
3702      }
3703      if (ref.navHover) {
3704        ref.removeEventListener('mousemove', ref.navHover);
3705        ref.removeEventListener('mouseleave', ref.navHideTooltip);
3706        delete ref.navHover;
3707        delete ref.navHideTooltip;
3708      }
3709      if (ref.navMouseMove) {
3710        document.removeEventListener('mousemove', ref.navMouseMove);
3711        document.removeEventListener('touchmove', ref.navMouseMove);
3712        delete ref.navMouseMove;
3713      }
3714      if (ref.navMouseUp) {
3715        document.removeEventListener('mouseup', ref.navMouseUp);
3716        document.removeEventListener('touchend', ref.navMouseUp);
3717        delete ref.navMouseUp;
3718      }
3719      if (ref.navResizeObserver) {
3720        ref.navResizeObserver.disconnect();
3721        delete ref.navResizeObserver;
3722      }
3723    };
3724    return [width, height];
3725  },
3726
3727    updateRangeNavigatorLabels: function (ref, isDate, inputStart, inputEnd, handleLabelFormatString) {
3728        if (!ref) return;
3729        ref.navLabelIsDate = isDate;
3730        ref.navLabelInputStart = inputStart;
3731        ref.navLabelInputEnd = inputEnd;
3732        ref.handleLabelFormatString = handleLabelFormatString;
3733    },
3734
3735    updateRangeNavigatorTooltip: function (ref, points) {
3736        if (!ref) return;
3737        ref.navTooltipPoints = points || [];
3738        if (!ref.navTooltipPoints.length && ref.navHideTooltip) ref.navHideTooltip();
3739    },
3740
3741  destroyGauge: function (ref) {
3742    if (ref._gaugeRTLObserver) {
3743      ref._gaugeRTLObserver.disconnect();
3744      delete ref._gaugeRTLObserver;
3745    }
3746    this.destroyResizable(ref);
3747  },
3748  disposeElement: function (ref) {
3749    if (ref && ref._radzenDispose) {
3750      ref._radzenDispose();
3751      delete ref._radzenDispose;
3752    }
3753  },
3754  destroyResizable: function (ref) {
3755    if (ref.resizeObserver) {
3756      ref.resizeObserver.disconnect();
3757      delete ref.resizeObserver;
3758    }
3759    if (ref.resizeHandler) {
3760      window.removeEventListener('resize', ref.resizeHandler);
3761      delete ref.resizeHandler;
3762    }
3763  },
3764  createResizable: function (ref, instance) {
3765    ref.resizeHandler = function () {
3766      var rect = ref.getBoundingClientRect();
3767
3768      try { suppressDisposed(instance.invokeMethodAsync('Resize', rect.width, rect.height)); } catch { }
3769    };
3770
3771    if (window.ResizeObserver) {
3772      ref.resizeObserver = new ResizeObserver(ref.resizeHandler);
3773      ref.resizeObserver.observe(ref);
3774    } else {
3775      window.addEventListener('resize', ref.resizeHandler);
3776    }
3777
3778    var self = this;
3779    ref._radzenDispose = function () { self.destroyResizable(ref); };
3780    var rect = ref.getBoundingClientRect();
3781    return { width: rect.width, height: rect.height };
3782  },
3783  chartToSvg: function (ref) {
3784    var svg = ref && ref.querySelector(':scope > svg');
3785    if (!svg) return '';
3786
3787    var clone = svg.cloneNode(true);
3788    var props = ['fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap',
3789      'stroke-linejoin', 'opacity', 'font-size', 'font-family', 'font-weight', 'font-variant-numeric',
3790      'text-anchor', 'dominant-baseline', 'stop-color', 'stop-opacity'];
3791    var bake = function (source, target) {
3792      var computed = getComputedStyle(source);
3793      var style = '';
3794      for (var i = 0; i < props.length; i++) {
3795        var value = computed.getPropertyValue(props[i]);
3796        if (value && value !== 'none' || props[i] === 'fill' || props[i] === 'stroke') {
3797          style += props[i] + ':' + value + ';';
3798        }
3799      }
3800      target.setAttribute('style', style);
3801    };
3802    var sources = svg.querySelectorAll('*');
3803    var targets = clone.querySelectorAll('*');
3804    for (var i = 0; i < sources.length; i++) {
3805      bake(sources[i], targets[i]);
3806    }
3807
3808    var rect = svg.getBoundingClientRect();
3809    clone.setAttribute('width', rect.width);
3810    clone.setAttribute('height', rect.height);
3811    clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
3812
3813    return new XMLSerializer().serializeToString(clone);
3814  },
3815  svgToPng: async function (svg, width, height) {
3816    if (!svg) return null;
3817
3818    var svgUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
3819    var img = new Image();
3820    await new Promise(function (resolve, reject) {
3821      img.onload = resolve;
3822      img.onerror = reject;
3823      img.src = svgUrl;
3824    });
3825
3826    var naturalWidth = img.naturalWidth || img.width;
3827    var naturalHeight = img.naturalHeight || img.height;
3828    var canvas = document.createElement('canvas');
3829    if (width > 0 || height > 0) {
3830      canvas.width = Math.round(width > 0 ? width : height * naturalWidth / naturalHeight);
3831      canvas.height = Math.round(height > 0 ? height : width * naturalHeight / naturalWidth);
3832    } else {
3833      var scale = Math.max(2, window.devicePixelRatio || 1);
3834      canvas.width = Math.round(naturalWidth * scale);
3835      canvas.height = Math.round(naturalHeight * scale);
3836    }
3837    var ctx = canvas.getContext('2d');
3838    var background = getComputedStyle(document.body).backgroundColor;
3839    if (background && background !== 'rgba(0, 0, 0, 0)') {
3840      ctx.fillStyle = background;
3841      ctx.fillRect(0, 0, canvas.width, canvas.height);
3842    }
3843    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
3844
3845    return await new Promise(function (resolve) {
3846      canvas.toBlob(resolve, 'image/png');
3847    });
3848  },
3849  downloadSvgAsPng: async function (svg, fileName, width, height) {
3850    var blob = await this.svgToPng(svg, width, height);
3851    if (blob) {
3852      this.downloadFile(fileName, blob, 'image/png');
3853    }
3854  },
3855  createChart: function (ref, instance, mouseMoveInterval) {
3856    var inside = false;
3857    var interval = mouseMoveInterval || 0;
3858    var lastDispatch = 0;
3859    var pendingMove = null;
3860    var moveRafId = null;
3861
3862    function dispatchMouseMove() {
3863      moveRafId = null;
3864      if (!pendingMove) return;
3865      var now = performance.now();
3866      if (interval > 0 && now - lastDispatch < interval) {
3867        moveRafId = requestAnimationFrame(dispatchMouseMove);
3868        return;
3869      }
3870      lastDispatch = now;
3871      var move = pendingMove;
3872      pendingMove = null;
3873      try { suppressDisposed(instance.invokeMethodAsync('MouseMove', move.x, move.y)); } catch { }
3874    }
3875
3876    ref.mouseMoveHandler = function (e) {
3877      if (inside) {
3878        var rect = ref.getBoundingClientRect();
3879        pendingMove = { x: e.clientX - rect.left, y: e.clientY - rect.top };
3880        if (!moveRafId) {
3881          moveRafId = requestAnimationFrame(dispatchMouseMove);
3882        }
3883      }
3884    };
3885    ref.mouseEnterHandler = function () {
3886        inside = true;
3887    };
3888    ref.mouseLeaveHandler = function (e) {
3889        if (e.relatedTarget && (e.relatedTarget.matches('.rz-chart-tooltip') || e.relatedTarget.closest('.rz-chart-tooltip'))) {
3890            return;
3891        }
3892        inside = false;
3893        try { suppressDisposed(instance.invokeMethodAsync('MouseMove', -1, -1)); } catch { }
3894    };
3895    ref.clickHandler = function (e) {
3896      var rect = ref.getBoundingClientRect();
3897      var x = e.clientX - rect.left;
3898      var y = e.clientY - rect.top;
3899      if (!e.target.closest('.rz-marker') && !e.target.closest('.rz-legend-item')) {
3900        try { suppressDisposed(instance.invokeMethodAsync('Click', x, y)); } catch { }
3901      }
3902    };
3903
3904    ref.wheelHandler = function (e) {
3905      if (!inside || ref.dataset.allowZoom !== 'true') return;
3906      var delta = e.deltaY > 0 ? 1 : -1;
3907      var range = parseFloat(ref.dataset.zoomEnd) - parseFloat(ref.dataset.zoomStart);
3908      if (isNaN(range)) range = 1;
3909      if (delta < 0 ? range <= 0.01 + 1e-6 : range >= 1 - 1e-6) return;
3910      e.preventDefault();
3911      var rect = ref.getBoundingClientRect();
3912      var x = e.clientX - rect.left;
3913      try { suppressDisposed(instance.invokeMethodAsync('OnWheel', x, delta)); } catch {}
3914    };
3915
3916    ref.addEventListener('mouseenter', ref.mouseEnterHandler);
3917    ref.addEventListener('mouseleave', ref.mouseLeaveHandler);
3918    ref.addEventListener('mousemove', ref.mouseMoveHandler);
3919    ref.addEventListener('click', ref.clickHandler);
3920    ref.addEventListener('wheel', ref.wheelHandler, { passive: false });
3921
3922    // Legend hover → series highlight (only when AllowSeriesHover is enabled)
3923    ref.legendEnterHandler = function (e) {
3924      if (!ref.classList.contains('rz-chart-series-hover')) return;
3925      var legendItem = e.target.closest('.rz-legend-item');
3926      if (!legendItem) return;
3927      var g = legendItem.querySelector('g[class]');
3928      if (!g) return;
3929      var classes = g.className.baseVal || g.getAttribute('class') || '';
3930      var match = classes.match(/rz-series-item-\d+|rz-series-\d+/);
3931      if (!match) return;
3932      var selector = '.' + match[0];
3933      var svg = ref.querySelector('svg');
3934      if (!svg) return;
3935      var targets = svg.querySelectorAll(selector);
3936      targets.forEach(function (t) { t.classList.add('rz-state-hover'); });
3937    };
3938    ref.legendLeaveHandler = function (e) {
3939      if (!ref.classList.contains('rz-chart-series-hover')) return;
3940      var legendItem = e.target.closest('.rz-legend-item');
3941      if (!legendItem) return;
3942      var svg = ref.querySelector('svg');
3943      if (!svg) return;
3944      var hovered = svg.querySelectorAll('.rz-state-hover');
3945      hovered.forEach(function (t) { t.classList.remove('rz-state-hover'); });
3946    };
3947    ref.addEventListener('mouseenter', ref.legendEnterHandler, true);
3948    ref.addEventListener('mouseleave', ref.legendLeaveHandler, true);
3949
3950    // Scrollbar drag handling via event delegation (survives Blazor re-renders)
3951    var dragging = false;
3952    var dragStartX = 0;
3953    var dragStartPos = 0;
3954
3955    function isChartRTL() {
3956      return document.documentElement.dir === 'rtl';
3957    }
3958
3959    function getThumbPos(thumb) {
3960      return parseFloat(isChartRTL() ? thumb.style.right : thumb.style.left) || 0;
3961    }
3962
3963    function setThumbPos(thumb, pos) {
3964      if (isChartRTL()) {
3965        thumb.style.right = pos + '%';
3966        thumb.style.left = '';
3967      } else {
3968        thumb.style.left = pos + '%';
3969        thumb.style.right = '';
3970      }
3971    }
3972
3973    ref.addEventListener('mousedown', function (e) {
3974      var thumb = e.target.closest('.rz-chart-scrollbar-thumb');
3975      if (!thumb) return;
3976      dragging = true;
3977      dragStartX = e.clientX;
3978      dragStartPos = getThumbPos(thumb);
3979      e.preventDefault();
3980    });
3981
3982    ref.scrollbarMoveHandler = function (e) {
3983      if (!dragging) return;
3984      var track = ref.querySelector('.rz-chart-scrollbar-track');
3985      var thumb = ref.querySelector('.rz-chart-scrollbar-thumb');
3986      if (!track || !thumb) return;
3987      var trackWidth = track.offsetWidth;
3988      if (trackWidth <= 0) return;
3989      var dx = e.clientX - dragStartX;
3990      var deltaPercent = (isChartRTL() ? -dx : dx) / trackWidth * 100;
3991      var newPos = Math.max(0, Math.min(100 - parseFloat(thumb.style.width), dragStartPos + deltaPercent));
3992      setThumbPos(thumb, newPos);
3993      var position = newPos / 100;
3994      try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
3995    };
3996    document.addEventListener('mousemove', ref.scrollbarMoveHandler);
3997
3998    ref.scrollbarUpHandler = function () {
3999      dragging = false;
4000    };
4001    document.addEventListener('mouseup', ref.scrollbarUpHandler);
4002
4003    ref.addEventListener('click', function (e) {
4004      var track = e.target.closest('.rz-chart-scrollbar-track');
4005      if (!track || e.target.closest('.rz-chart-scrollbar-thumb')) return;
4006      var thumb = track.querySelector('.rz-chart-scrollbar-thumb');
4007      if (!thumb) return;
4008      var trackRect = track.getBoundingClientRect();
4009      var clickX = isChartRTL() ? trackRect.right - e.clientX : e.clientX - trackRect.left;
4010      var thumbWidth = thumb.offsetWidth;
4011      var position = Math.max(0, Math.min(1 - thumbWidth / track.offsetWidth, (clickX - thumbWidth / 2) / track.offsetWidth));
4012      try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4013    });
4014
4015    // Touch support for scrollbar drag
4016    ref.addEventListener('touchstart', function (e) {
4017      var thumb = e.target.closest('.rz-chart-scrollbar-thumb');
4018      if (!thumb) return;
4019      dragging = true;
4020      dragStartX = e.touches[0].clientX;
4021      dragStartPos = getThumbPos(thumb);
4022      e.preventDefault();
4023    }, { passive: false });
4024
4025    ref.scrollbarTouchMoveHandler = function (e) {
4026      if (!dragging) return;
4027      var track = ref.querySelector('.rz-chart-scrollbar-track');
4028      var thumb = ref.querySelector('.rz-chart-scrollbar-thumb');
4029      if (!track || !thumb) return;
4030      var trackWidth = track.offsetWidth;
4031      if (trackWidth <= 0) return;
4032      var dx = e.touches[0].clientX - dragStartX;
4033      var deltaPercent = (isChartRTL() ? -dx : dx) / trackWidth * 100;
4034      var newPos = Math.max(0, Math.min(100 - parseFloat(thumb.style.width), dragStartPos + deltaPercent));
4035      setThumbPos(thumb, newPos);
4036      var position = newPos / 100;
4037      try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4038      e.preventDefault();
4039    };
4040    document.addEventListener('touchmove', ref.scrollbarTouchMoveHandler, { passive: false });
4041
4042    ref.scrollbarTouchEndHandler = function () {
4043      dragging = false;
4044    };
4045    document.addEventListener('touchend', ref.scrollbarTouchEndHandler);
4046
4047    // Touch pan and pinch-to-zoom support
4048    var pinchStartDistance = 0;
4049    var pinchActive = false;
4050    var touchPanning = false;
4051    var touchPanStartX = 0;
4052
4053    ref.touchStartHandler = function (e) {
4054      if (e.touches.length === 2) {
4055        // Two-finger: pinch-to-zoom
4056        pinchActive = true;
4057        touchPanning = false;
4058        var dx = e.touches[0].clientX - e.touches[1].clientX;
4059        var dy = e.touches[0].clientY - e.touches[1].clientY;
4060        pinchStartDistance = Math.sqrt(dx * dx + dy * dy);
4061        e.preventDefault();
4062      } else if (e.touches.length === 1 && !e.target.closest('.rz-chart-scrollbar')) {
4063        // Single-finger on chart area: pan
4064        touchPanning = true;
4065        touchPanStartX = e.touches[0].clientX;
4066      }
4067    };
4068    ref.addEventListener('touchstart', ref.touchStartHandler, { passive: false });
4069
4070    ref.touchMoveHandler = function (e) {
4071      if (pinchActive && e.touches.length === 2) {
4072        // Pinch-to-zoom
4073        var dx = e.touches[0].clientX - e.touches[1].clientX;
4074        var dy = e.touches[0].clientY - e.touches[1].clientY;
4075        var currentDistance = Math.sqrt(dx * dx + dy * dy);
4076        if (pinchStartDistance <= 0) return;
4077        var midX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
4078        var rect = ref.getBoundingClientRect();
4079        var x = midX - rect.left;
4080        var ratio = currentDistance / pinchStartDistance;
4081        if (ratio > 1.05) {
4082          try { suppressDisposed(instance.invokeMethodAsync('OnWheel', x, -1)); } catch {}
4083          pinchStartDistance = currentDistance;
4084        } else if (ratio < 0.95) {
4085          try { suppressDisposed(instance.invokeMethodAsync('OnWheel', x, 1)); } catch {}
4086          pinchStartDistance = currentDistance;
4087        }
4088        e.preventDefault();
4089      } else if (touchPanning && e.touches.length === 1) {
4090        // Single-finger pan
4091        var rect = ref.getBoundingClientRect();
4092        var chartWidth = rect.width;
4093        if (chartWidth <= 0) return;
4094        var dx = e.touches[0].clientX - touchPanStartX;
4095        var thumb = ref.querySelector('.rz-chart-scrollbar-thumb');
4096        if (!thumb) return;
4097        var thumbWidth = parseFloat(thumb.style.width) || 50;
4098        var currentPos = getThumbPos(thumb);
4099        // Swipe in reading direction = move view backward, swipe against = move view forward
4100        var deltaPercent = (isChartRTL() ? dx : -dx) / chartWidth * 100;
4101        var newPos = Math.max(0, Math.min(100 - thumbWidth, currentPos + deltaPercent));
4102        var position = newPos / 100;
4103        try { suppressDisposed(instance.invokeMethodAsync('OnPan', position)); } catch {}
4104        touchPanStartX = e.touches[0].clientX;
4105        e.preventDefault();
4106      }
4107    };
4108    ref.addEventListener('touchmove', ref.touchMoveHandler, { passive: false });
4109
4110    ref.touchEndHandler = function (e) {
4111      if (e.touches.length < 2) {
4112        pinchActive = false;
4113      }
4114      if (e.touches.length === 0) {
4115        touchPanning = false;
4116      }
4117    };
4118    ref.addEventListener('touchend', ref.touchEndHandler);
4119
4120    // RTL detection
4121    ref._chartRTLObserver = new MutationObserver(function () {
4122      try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4123    });
4124    ref._chartRTLObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });
4125    try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4126
4127    this.createResizable(ref, instance);
4128    var self = this;
4129    ref._radzenDispose = function () { self.destroyChart(ref); };
4130    var rect = ref.getBoundingClientRect();
4131    return { width: rect.width, height: rect.height };
4132  },
4133  createGauge: function (ref, instance) {
4134    this.createResizable(ref, instance);
4135    ref._gaugeRTLObserver = new MutationObserver(function () {
4136      try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4137    });
4138    ref._gaugeRTLObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['dir'] });
4139    try { suppressDisposed(instance.invokeMethodAsync('SetRTL', document.documentElement.dir === 'rtl')); } catch (e) { }
4140    var rect = ref.getBoundingClientRect();
4141    return { width: rect.width, height: rect.height };
4142  },
4143  innerHTML: function (ref, value) {
4144    if (value != undefined) {
4145        if (ref != null) {
4146            ref.innerHTML = value;
4147        }
4148    } else {
4149      return ref.innerHTML;
4150    }
4151  },
4152  execCommand: function (ref, name, value) {
4153    if (document.activeElement != ref && ref) {
4154      ref.focus();
4155    }
4156    document.execCommand(name, false, value);
4157    return this.queryCommands(ref);
4158  },
4159  formatTableColumnHeader: function (ref, index) {
4160    var format = (ref && ref.tableColumnHeaderFormat) || 'Column {0}';
4161    return format.replace('{0}', (index + 1).toString());
4162  },
4163  createTableHTML: function (ref, options) {
4164    options = options || {};
4165    var rows = Math.max(parseInt(options.rows || 0, 10) || 0, 1);
4166    var columns = Math.max(parseInt(options.columns || 0, 10) || 0, 1);
4167    var width = options.width || '100%';
4168    var border = Math.max(parseInt(options.border || 0, 10) || 0, 0);
4169    var headerRow = !!options.headerRow;
4170    var html = '<table style="border-collapse: collapse; width: ' + width + ';" border="' + border + '">';
4171
4172    if (headerRow) {
4173      html += '<thead><tr>';
4174      for (var headerIndex = 0; headerIndex < columns; headerIndex++) {
4175        html += '<th>' + this.formatTableColumnHeader(ref, headerIndex) + '</th>';
4176      }
4177      html += '</tr></thead>';
4178    }
4179
4180    html += '<tbody>';
4181    for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
4182      html += '<tr>';
4183      for (var columnIndex = 0; columnIndex < columns; columnIndex++) {
4184        html += '<td>&nbsp;</td>';
4185      }
4186      html += '</tr>';
4187    }
4188    html += '</tbody></table><p></p>';
4189
4190    return html;
4191  },
4192  buildTableContext: function (ref, element) {
4193    if (!ref) {
4194      return { ref: ref };
4195    }
4196
4197    if (!element || !ref.contains(element)) {
4198      return { ref: ref };
4199    }
4200
4201    var cell = element.closest('td,th');
4202    var table = element.closest('table');
4203
4204    if (!cell || !table || !ref.contains(table)) {
4205      return { ref: ref, table: table };
4206    }
4207
4208    var row = cell.parentElement;
4209    var tbody = table.tBodies && table.tBodies.length ? table.tBodies[0] : null;
4210    var thead = table.tHead;
4211    var rowIndex = -1;
4212
4213    if (tbody && tbody.contains(row)) {
4214      rowIndex = Array.prototype.indexOf.call(tbody.rows, row);
4215    }
4216
4217    return {
4218      ref: ref,
4219      table: table,
4220      cell: cell,
4221      row: row,
4222      tbody: tbody,
4223      thead: thead,
4224      inHeader: !!(thead && thead.contains(cell)),
4225      rowIndex: rowIndex,
4226      columnIndex: Array.prototype.indexOf.call(row.children, cell)
4227    };
4228  },
4229  getTableContext: function (ref) {
4230    var selection = getSelection();
4231    var range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
4232    var node = selection && selection.focusNode ? selection.focusNode : range && range.commonAncestorContainer;
4233
4234    if (!node) {
4235      return { ref: ref };
4236    }
4237
4238    return this.buildTableContext(ref, node.nodeType === 3 ? node.parentElement : node);
4239  },
4240  getCellMatrix: function (section) {
4241    var matrix = [];
4242    if (!section || !section.rows) {
4243      return matrix;
4244    }
4245
4246    for (var rowIndex = 0; rowIndex < section.rows.length; rowIndex++) {
4247      var row = section.rows[rowIndex];
4248      if (!matrix[rowIndex]) {
4249        matrix[rowIndex] = [];
4250      }
4251
4252      var columnIndex = 0;
4253      for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
4254        var cell = row.cells[cellIndex];
4255        while (matrix[rowIndex][columnIndex]) {
4256          columnIndex++;
4257        }
4258
4259        var colSpan = Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1);
4260        var rowSpan = Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1);
4261
4262        for (var r = 0; r < rowSpan; r++) {
4263          var targetRow = rowIndex + r;
4264          if (!matrix[targetRow]) {
4265            matrix[targetRow] = [];
4266          }
4267
4268          for (var c = 0; c < colSpan; c++) {
4269            matrix[targetRow][columnIndex + c] = cell;
4270          }
4271        }
4272
4273        columnIndex += colSpan;
4274      }
4275    }
4276
4277    return matrix;
4278  },
4279  getCellPosition: function (section, cell, matrix) {
4280    matrix = matrix || this.getCellMatrix(section);
4281
4282    for (var rowIndex = 0; rowIndex < matrix.length; rowIndex++) {
4283      for (var columnIndex = 0; columnIndex < (matrix[rowIndex] || []).length; columnIndex++) {
4284        if (matrix[rowIndex][columnIndex] === cell) {
4285          return { rowIndex: rowIndex, columnIndex: columnIndex };
4286        }
4287      }
4288    }
4289
4290    return { rowIndex: -1, columnIndex: -1 };
4291  },
4292  clearSelectedTableCells: function (ref) {
4293    if (!ref) {
4294      return;
4295    }
4296
4297    Array.prototype.forEach.call(ref.querySelectorAll('td.rz-html-editor-cell-selected,th.rz-html-editor-cell-selected'), function (cell) {
4298      cell.classList.remove('rz-html-editor-cell-selected');
4299    });
4300  },
4301  getSelectedTableCells: function (ref) {
4302    if (!ref) {
4303      return [];
4304    }
4305
4306    return Array.prototype.filter.call(ref.querySelectorAll('td.rz-html-editor-cell-selected,th.rz-html-editor-cell-selected'), function (cell) {
4307      return ref.contains(cell);
4308    });
4309  },
4310  selectTableCellRange: function (ref, startCell, endCell) {
4311    if (!ref || !startCell || !endCell) {
4312      return false;
4313    }
4314
4315    var startSection = startCell.parentElement ? startCell.parentElement.parentElement : null;
4316    var endSection = endCell.parentElement ? endCell.parentElement.parentElement : null;
4317
4318    if (!startSection || startSection !== endSection) {
4319      return false;
4320    }
4321
4322    var matrix = this.getCellMatrix(startSection);
4323    var start = this.getCellPosition(startSection, startCell, matrix);
4324    var end = this.getCellPosition(startSection, endCell, matrix);
4325
4326    if (start.rowIndex < 0 || start.columnIndex < 0 || end.rowIndex < 0 || end.columnIndex < 0) {
4327      return false;
4328    }
4329
4330    var minRow = Math.min(start.rowIndex, end.rowIndex);
4331    var maxRow = Math.max(start.rowIndex, end.rowIndex);
4332    var minColumn = Math.min(start.columnIndex, end.columnIndex);
4333    var maxColumn = Math.max(start.columnIndex, end.columnIndex);
4334    this.clearSelectedTableCells(ref);
4335
4336    for (var rowIndex = minRow; rowIndex <= maxRow; rowIndex++) {
4337      for (var columnIndex = minColumn; columnIndex <= maxColumn; columnIndex++) {
4338        var cell = (matrix[rowIndex] || [])[columnIndex];
4339        if (cell) {
4340          cell.classList.add('rz-html-editor-cell-selected');
4341        }
4342      }
4343    }
4344
4345    return true;
4346  },
4347  getSelectedCellBounds: function (ref, section) {
4348    var selectedCells = this.getSelectedTableCells(ref);
4349    if (!selectedCells.length) {
4350      return null;
4351    }
4352
4353    var matrix = this.getCellMatrix(section);
4354    var minRow = Number.MAX_SAFE_INTEGER;
4355    var maxRow = -1;
4356    var minColumn = Number.MAX_SAFE_INTEGER;
4357    var maxColumn = -1;
4358
4359    selectedCells.forEach(function (selectedCell) {
4360      var position = Radzen.getCellPosition(section, selectedCell, matrix);
4361      var rowIndex = position.rowIndex;
4362      var columnIndex = position.columnIndex;
4363      if (rowIndex < 0 || columnIndex < 0) {
4364        return;
4365      }
4366
4367      var rowSpan = Math.max(parseInt(selectedCell.getAttribute('rowspan') || 1, 10) || 1, 1);
4368      var colSpan = Math.max(parseInt(selectedCell.getAttribute('colspan') || 1, 10) || 1, 1);
4369
4370      minRow = Math.min(minRow, rowIndex);
4371      maxRow = Math.max(maxRow, rowIndex + rowSpan - 1);
4372      minColumn = Math.min(minColumn, columnIndex);
4373      maxColumn = Math.max(maxColumn, columnIndex + colSpan - 1);
4374    });
4375
4376    if (minRow === Number.MAX_SAFE_INTEGER || minColumn === Number.MAX_SAFE_INTEGER) {
4377      return null;
4378    }
4379
4380    for (var rowIndex = minRow; rowIndex <= maxRow; rowIndex++) {
4381      for (var columnIndex = minColumn; columnIndex <= maxColumn; columnIndex++) {
4382        var candidate = (matrix[rowIndex] || [])[columnIndex];
4383        if (!candidate || selectedCells.indexOf(candidate) === -1) {
4384          return null;
4385        }
4386      }
4387    }
4388
4389    return {
4390      selectedCells: selectedCells,
4391      matrix: matrix,
4392      minRow: minRow,
4393      maxRow: maxRow,
4394      minColumn: minColumn,
4395      maxColumn: maxColumn
4396    };
4397  },
4398  copySelectedTableCells: function (ref) {
4399    var cells = this.getSelectedTableCells(ref);
4400    if (!cells.length) {
4401      return false;
4402    }
4403
4404    var section = cells[0].parentElement ? cells[0].parentElement.parentElement : null;
4405    var bounds = this.getSelectedCellBounds(ref, section);
4406    if (!bounds) {
4407      return false;
4408    }
4409
4410    var copiedMatrix = [];
4411    for (var rowIndex = bounds.minRow; rowIndex <= bounds.maxRow; rowIndex++) {
4412      copiedMatrix.push([]);
4413      for (var columnIndex = bounds.minColumn; columnIndex <= bounds.maxColumn; columnIndex++) {
4414        var cell = (bounds.matrix[rowIndex] || [])[columnIndex];
4415        copiedMatrix[copiedMatrix.length - 1].push(bounds.selectedCells.indexOf(cell) > -1 && cell ? cell.outerHTML : null);
4416      }
4417    }
4418
4419    ref.copiedCells = {
4420      matrix: copiedMatrix,
4421      rows: copiedMatrix.length,
4422      columns: copiedMatrix.length ? copiedMatrix[0].length : 0
4423    };
4424
4425    return true;
4426  },
4427  pasteSelectedTableCells: function (ref, targetCell) {
4428    if (!ref || !ref.copiedCells || !ref.copiedCells.matrix || !targetCell || !targetCell.parentElement) {
4429      return false;
4430    }
4431
4432    var section = targetCell.parentElement.parentElement;
4433    var matrix = this.getCellMatrix(section);
4434    var position = this.getCellPosition(section, targetCell, matrix);
4435    var startRow = position.rowIndex;
4436    var startColumn = position.columnIndex;
4437
4438    if (startRow < 0 || startColumn < 0) {
4439      return false;
4440    }
4441
4442    for (var guardRowOffset = 0; guardRowOffset < ref.copiedCells.rows; guardRowOffset++) {
4443      for (var guardColumnOffset = 0; guardColumnOffset < ref.copiedCells.columns; guardColumnOffset++) {
4444        var guardCell = (matrix[startRow + guardRowOffset] || [])[startColumn + guardColumnOffset];
4445        if (guardCell) {
4446          var guardCellPosition = this.getCellPosition(section, guardCell, matrix);
4447          var guardRowSpan = Math.max(parseInt(guardCell.getAttribute('rowspan') || 1, 10) || 1, 1);
4448          var guardColSpan = Math.max(parseInt(guardCell.getAttribute('colspan') || 1, 10) || 1, 1);
4449          if ((guardRowSpan > 1 || guardColSpan > 1) && (guardCellPosition.rowIndex !== startRow + guardRowOffset || guardCellPosition.columnIndex !== startColumn + guardColumnOffset)) {
4450            return false;
4451          }
4452        }
4453      }
4454    }
4455
4456    for (var rowOffset = 0; rowOffset < ref.copiedCells.rows; rowOffset++) {
4457      var targetRow = section.rows[startRow + rowOffset];
4458      if (!targetRow) {
4459        if (section.tagName === 'TBODY') {
4460          targetRow = section.insertRow();
4461        } else {
4462          return false;
4463        }
4464      }
4465
4466      for (var columnOffset = 0; columnOffset < ref.copiedCells.columns; columnOffset++) {
4467        var target = (this.getCellMatrix(section)[startRow + rowOffset] || [])[startColumn + columnOffset];
4468        if (!target) {
4469          target = document.createElement(targetCell.tagName.toLowerCase());
4470          target.innerHTML = '&nbsp;';
4471          targetRow.appendChild(target);
4472        }
4473
4474        var html = ref.copiedCells.matrix[rowOffset][columnOffset];
4475        if (html) {
4476          var template = document.createElement('template');
4477          template.innerHTML = html.trim();
4478          var sourceCell = template.content.firstElementChild;
4479          if (sourceCell) {
4480            target.innerHTML = sourceCell.innerHTML;
4481          }
4482        }
4483      }
4484    }
4485
4486    return true;
4487  },
4488  createCommandResult: function (ref, success, message) {
4489    var result = this.queryCommands(ref);
4490    result.success = success;
4491    result.message = message || null;
4492    return result;
4493  },
4494  getContextTableSelection: function (ref) {
4495    var context = ref && ref.contextMenuTarget ? this.getTableContextFromTarget(ref, ref.contextMenuTarget) : this.getTableContext(ref);
4496    if (!context || !context.table) {
4497      return this.getTableSelection(ref);
4498    }
4499
4500    return this.getTableSelectionFromContext(ref, context);
4501  },
4502  getTableContextFromTarget: function (ref, target) {
4503    if (!target) {
4504      return this.getTableContext(ref);
4505    }
4506
4507    return this.buildTableContext(ref, target.nodeType === 3 ? target.parentElement : target);
4508  },
4509  getTableSelectionFromContext: function (ref, context) {
4510    var table = context.table;
4511
4512    if (!table) {
4513      return this.getTableSelection(ref);
4514    }
4515
4516    var cell = context.cell;
4517    var section = context.inHeader && table.tHead ? table.tHead : (context.row ? context.row.parentElement : (table.tBodies[0] || table));
4518    var matrix = this.getCellMatrix(section);
4519    var position = cell ? this.getCellPosition(section, cell, matrix) : { rowIndex: -1, columnIndex: context.columnIndex };
4520    var rowIndex = position.rowIndex;
4521    var columnIndex = position.columnIndex;
4522    var colSpan = cell ? Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1) : 1;
4523    var rowSpan = cell ? Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1) : 1;
4524    var rowCells = matrix[rowIndex] || [];
4525    var rightCell = rowCells[columnIndex + colSpan];
4526    var belowRow = matrix[rowIndex + rowSpan] || [];
4527    var canMergeDown = true;
4528
4529    for (var i = 0; i < colSpan; i++) {
4530      if (!belowRow[columnIndex + i]) {
4531        canMergeDown = false;
4532        break;
4533      }
4534    }
4535
4536    var bodyRows = table.tBodies && table.tBodies.length ? table.tBodies[0].rows.length : 0;
4537    var firstRow = table.rows && table.rows.length ? table.rows[0] : null;
4538    var border = parseInt(table.getAttribute('border') || 0, 10) || 0;
4539    var columnWidth = cell && cell.style ? cell.style.width || null : null;
4540    var cellBackground = cell && cell.style ? cell.style.backgroundColor || null : null;
4541    var cellPadding = cell && cell.style ? cell.style.padding || null : null;
4542    var cellTextAlign = cell && cell.style ? cell.style.textAlign || null : null;
4543    var cellVerticalAlign = cell && cell.style ? cell.style.verticalAlign || null : null;
4544    var cellBorder = cell && cell.style ? cell.style.border || null : null;
4545    var columnWidthPx = columnWidth ? parseInt(columnWidth, 10) || null : null;
4546    var cellPaddingPx = cellPadding ? parseInt(cellPadding, 10) || null : null;
4547    var borderTop = !!(cell && cell.style && cell.style.borderTop !== 'none' && cell.style.borderTop !== '');
4548    var borderRight = !!(cell && cell.style && cell.style.borderRight !== 'none' && cell.style.borderRight !== '');
4549    var borderBottom = !!(cell && cell.style && cell.style.borderBottom !== 'none' && cell.style.borderBottom !== '');
4550    var borderLeft = !!(cell && cell.style && cell.style.borderLeft !== 'none' && cell.style.borderLeft !== '');
4551    var borderStyle = cell && cell.style ? (cell.style.borderStyle || (cellBorder ? cellBorder.split(' ')[1] : null)) : null;
4552    var borderWidthPx = cell && cell.style && cell.style.borderWidth ? parseInt(cell.style.borderWidth, 10) || null : (cellBorder ? parseInt(cellBorder, 10) || null : null);
4553    var borderColor = cell && cell.style ? (cell.style.borderColor || null) : null;
4554
4555    return {
4556      inTable: true,
4557      inHeader: !!context.inHeader,
4558      rowIndex: context.rowIndex,
4559      columnIndex: columnIndex,
4560      rows: bodyRows,
4561      columns: firstRow ? firstRow.children.length : 0,
4562      width: table.style.width || table.getAttribute('width') || '100%',
4563      border: border,
4564      headerRow: !!(table.tHead && table.tHead.rows.length),
4565      colSpan: colSpan,
4566      rowSpan: rowSpan,
4567      canMergeRight: !!rightCell && rightCell !== cell,
4568      canMergeDown: canMergeDown,
4569      canSplit: colSpan > 1 || rowSpan > 1,
4570      columnWidth: columnWidth,
4571      cellBackground: cellBackground,
4572      cellPadding: cellPadding,
4573      cellTextAlign: cellTextAlign,
4574      cellVerticalAlign: cellVerticalAlign,
4575      cellBorder: cellBorder,
4576      columnWidthPx: columnWidthPx,
4577      cellPaddingPx: cellPaddingPx,
4578      borderStyle: borderStyle,
4579      borderWidthPx: borderWidthPx,
4580      borderColor: borderColor,
4581      borderTop: borderTop,
4582      borderRight: borderRight,
4583      borderBottom: borderBottom,
4584      borderLeft: borderLeft
4585    };
4586  },
4587  getTableSelection: function (ref) {
4588    var context = this.getTableContext(ref);
4589    if (!context.table) {
4590      return {
4591        inTable: false,
4592        inHeader: false,
4593        rowIndex: -1,
4594        columnIndex: -1,
4595        rows: 0,
4596        columns: 0,
4597        width: null,
4598        border: 0,
4599        headerRow: false,
4600        colSpan: 1,
4601        rowSpan: 1,
4602        canMergeRight: false,
4603        canMergeDown: false,
4604        canSplit: false,
4605        columnWidth: null,
4606        columnWidthPx: null,
4607        cellBackground: null,
4608        cellPadding: null,
4609        cellPaddingPx: null,
4610        cellTextAlign: null,
4611        cellVerticalAlign: null,
4612        cellBorder: null,
4613        borderStyle: null,
4614        borderWidthPx: null,
4615        borderColor: null,
4616        borderTop: false,
4617        borderRight: false,
4618        borderBottom: false,
4619        borderLeft: false
4620      };
4621    }
4622
4623    return this.getTableSelectionFromContext(ref, context);
4624  },
4625  ensureTableBody: function (table) {
4626    if (!table.tBodies.length) {
4627      table.appendChild(document.createElement('tbody'));
4628    }
4629    return table.tBodies[0];
4630  },
4631  getTableColumnCount: function (table) {
4632    var firstRow = table.rows.length ? table.rows[0] : null;
4633    return firstRow ? firstRow.children.length : 0;
4634  },
4635  createTableRow: function (ref, columns, header) {
4636    var tr = document.createElement('tr');
4637    for (var i = 0; i < columns; i++) {
4638      var element = document.createElement(header ? 'th' : 'td');
4639      element.innerHTML = header ? this.formatTableColumnHeader(ref, i) : '&nbsp;';
4640      tr.appendChild(element);
4641    }
4642    return tr;
4643  },
4644  updateTable: function (context, value) {
4645    value = value || {};
4646    var table = context.table;
4647    var cell = context.cell;
4648    var row = context.row;
4649    var columns = Math.max(parseInt(value.columns || 0, 10) || 0, 1);
4650    var rows = Math.max(parseInt(value.rows || 0, 10) || 0, 1);
4651    var border = Math.max(parseInt(value.border || 0, 10) || 0, 0);
4652    var columnWidth = value.columnWidthPx ? value.columnWidthPx + 'px' : (value.columnWidth || '');
4653    var cellPadding = value.cellPaddingPx != null ? value.cellPaddingPx + 'px' : (value.cellPadding || '');
4654    var borderStyle = value.borderStyle || 'solid';
4655    var borderWidth = value.borderWidthPx != null ? value.borderWidthPx + 'px' : '1px';
4656    var borderColor = value.borderColor || 'currentColor';
4657
4658    table.style.width = value.width || '100%';
4659    if (!table.style.borderCollapse) {
4660      table.style.borderCollapse = 'collapse';
4661    }
4662    table.setAttribute('border', border.toString());
4663
4664    this.updateTableHeader(context.ref, table, columns, !!value.headerRow);
4665    this.updateTableBody(context.ref, table, rows, columns);
4666
4667    if (cell) {
4668      this.updateTableCellStyles(row, cell, context, {
4669        columnWidth: columnWidth,
4670        cellBackground: value.cellBackground || '',
4671        cellPadding: cellPadding,
4672        cellTextAlign: value.cellTextAlign || '',
4673        cellVerticalAlign: value.cellVerticalAlign || '',
4674        cellBorder: value.cellBorder || '',
4675        borderValue: borderWidth + ' ' + borderStyle + ' ' + borderColor,
4676        borderTop: value.borderTop,
4677        borderRight: value.borderRight,
4678        borderBottom: value.borderBottom,
4679        borderLeft: value.borderLeft
4680      });
4681    }
4682  },
4683  updateTableHeader: function (ref, table, columns, headerRow) {
4684    if (headerRow) {
4685      var thead = table.tHead || table.createTHead();
4686      var header = thead.rows.length ? thead.rows[0] : thead.insertRow();
4687      while (header.children.length < columns) {
4688        var th = document.createElement('th');
4689        th.innerHTML = this.formatTableColumnHeader(ref, header.children.length);
4690        header.appendChild(th);
4691      }
4692      while (header.children.length > columns) {
4693        header.removeChild(header.lastElementChild);
4694      }
4695      for (var h = 0; h < header.children.length; h++) {
4696        if (!header.children[h].innerHTML || header.children[h].innerHTML === '&nbsp;') {
4697          header.children[h].innerHTML = this.formatTableColumnHeader(ref, h);
4698        }
4699      }
4700    } else if (table.tHead) {
4701      table.deleteTHead();
4702    }
4703  },
4704  updateTableBody: function (ref, table, rows, columns) {
4705    var tbody = this.ensureTableBody(table);
4706    while (tbody.rows.length < rows) {
4707      tbody.appendChild(this.createTableRow(ref, columns, false));
4708    }
4709    while (tbody.rows.length > rows) {
4710      tbody.removeChild(tbody.lastElementChild);
4711    }
4712    for (var r = 0; r < tbody.rows.length; r++) {
4713      var bodyRow = tbody.rows[r];
4714      while (bodyRow.children.length < columns) {
4715        var td = document.createElement('td');
4716        td.innerHTML = '&nbsp;';
4717        bodyRow.appendChild(td);
4718      }
4719      while (bodyRow.children.length > columns) {
4720        bodyRow.removeChild(bodyRow.lastElementChild);
4721      }
4722    }
4723  },
4724  updateTableCellStyles: function (row, cell, context, options) {
4725    var sectionMatrix = row && row.parentElement ? this.getCellMatrix(row.parentElement) : [];
4726    var resizePosition = row && row.parentElement ? this.getCellPosition(row.parentElement, cell, sectionMatrix) : { columnIndex: context.columnIndex };
4727    var resizeColumnIndex = resizePosition.columnIndex;
4728
4729    for (var rowIndex = 0; rowIndex < sectionMatrix.length; rowIndex++) {
4730      var syncCell = sectionMatrix[rowIndex][resizeColumnIndex];
4731      if (syncCell && options.columnWidth) {
4732        syncCell.style.width = options.columnWidth;
4733      } else if (syncCell) {
4734        syncCell.style.removeProperty('width');
4735      }
4736    }
4737
4738    options.cellBackground ? cell.style.backgroundColor = options.cellBackground : cell.style.removeProperty('background-color');
4739    options.cellPadding ? cell.style.padding = options.cellPadding : cell.style.removeProperty('padding');
4740    options.cellTextAlign ? cell.style.textAlign = options.cellTextAlign : cell.style.removeProperty('text-align');
4741    options.cellVerticalAlign ? cell.style.verticalAlign = options.cellVerticalAlign : cell.style.removeProperty('vertical-align');
4742
4743    if (options.cellBorder) {
4744      cell.style.border = options.cellBorder;
4745    } else {
4746      cell.style.borderTop = options.borderTop ? options.borderValue : 'none';
4747      cell.style.borderRight = options.borderRight ? options.borderValue : 'none';
4748      cell.style.borderBottom = options.borderBottom ? options.borderValue : 'none';
4749      cell.style.borderLeft = options.borderLeft ? options.borderValue : 'none';
4750    }
4751  },
4752  addTableRow: function (context, command) {
4753    var row = context.row;
4754    var newRow = this.createTableRow(context.ref, this.getTableColumnCount(context.table), context.inHeader);
4755    row.parentNode.insertBefore(newRow, command === 'addRowBefore' ? row : row.nextSibling);
4756  },
4757  deleteTableRow: function (context) {
4758    var table = context.table;
4759    if (context.row) {
4760      context.row.remove();
4761    }
4762    if (table.tHead && !table.tHead.rows.length) {
4763      table.deleteTHead();
4764    }
4765    if (table.tBodies.length && !table.tBodies[0].rows.length) {
4766      table.tBodies[0].appendChild(this.createTableRow(context.ref, this.getTableColumnCount(table) || 1, false));
4767    }
4768  },
4769  addTableColumn: function (context, command) {
4770    var self = this;
4771    var ref = context.ref;
4772    var table = context.table;
4773    var columnIndex = Math.max(context.columnIndex, 0);
4774    var insertIndex = command === 'addColumnBefore' ? columnIndex : columnIndex + 1;
4775    Array.prototype.forEach.call(table.rows, function (tableRow) {
4776      var header = table.tHead && table.tHead.contains(tableRow);
4777      var column = document.createElement(header ? 'th' : 'td');
4778      column.innerHTML = header ? self.formatTableColumnHeader(ref, insertIndex) : '&nbsp;';
4779      tableRow.insertBefore(column, tableRow.children[insertIndex] || null);
4780    });
4781  },
4782  deleteTableColumn: function (context) {
4783    var index = context.columnIndex;
4784    if (index >= 0) {
4785      Array.prototype.forEach.call(context.table.rows, function (tableRow) {
4786        if (tableRow.children.length > 1 && tableRow.children[index]) {
4787          tableRow.removeChild(tableRow.children[index]);
4788        }
4789      });
4790    }
4791  },
4792  mergeTableCellRight: function (ref, context) {
4793    var row = context.row;
4794    var cell = context.cell;
4795    var sectionRight = row && row.parentElement ? row.parentElement : null;
4796    var boundsRight = this.getSelectedCellBounds(ref, sectionRight);
4797    if (boundsRight && boundsRight.selectedCells.length > 1) {
4798      var anchorCellRight = (boundsRight.matrix[boundsRight.minRow] || [])[boundsRight.minColumn];
4799      if (!anchorCellRight) {
4800        return false;
4801      }
4802      anchorCellRight.setAttribute('colspan', Math.max(boundsRight.maxColumn - boundsRight.minColumn + 1, 1));
4803      anchorCellRight.setAttribute('rowspan', Math.max(boundsRight.maxRow - boundsRight.minRow + 1, 1));
4804      for (var mergeRow = boundsRight.minRow; mergeRow <= boundsRight.maxRow; mergeRow++) {
4805        for (var mergeCol = boundsRight.minColumn; mergeCol <= boundsRight.maxColumn; mergeCol++) {
4806          var mergedCell = (boundsRight.matrix[mergeRow] || [])[mergeCol];
4807          if (mergedCell && mergedCell !== anchorCellRight && mergedCell.parentElement) {
4808            mergedCell.remove();
4809          }
4810        }
4811      }
4812      return true;
4813    }
4814    if (cell && row && row.parentElement) {
4815      var rightMatrix = this.getCellMatrix(row.parentElement);
4816      var rightPosition = this.getCellPosition(row.parentElement, cell, rightMatrix);
4817      var currentRightSpan = Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1);
4818      var nextCell = (rightMatrix[rightPosition.rowIndex] || [])[rightPosition.columnIndex + currentRightSpan];
4819      if (!nextCell || nextCell === cell) {
4820        return true;
4821      }
4822      cell.setAttribute('colspan', currentRightSpan + Math.max(parseInt(nextCell.getAttribute('colspan') || 1, 10) || 1, 1));
4823      if (!cell.innerHTML || cell.innerHTML === '&nbsp;') {
4824        cell.innerHTML = nextCell.innerHTML;
4825      } else if (nextCell.innerHTML && nextCell.innerHTML !== '&nbsp;') {
4826        cell.innerHTML += ' ' + nextCell.innerHTML;
4827      }
4828      nextCell.remove();
4829    }
4830    return true;
4831  },
4832  mergeTableCellDown: function (context) {
4833    var cell = context.cell;
4834    var row = context.row;
4835    if (cell && row && row.parentElement) {
4836      var downMatrix = this.getCellMatrix(row.parentElement);
4837      var downPosition = this.getCellPosition(row.parentElement, cell, downMatrix);
4838      var currentRowSpan = Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1);
4839      var targetCell = (downMatrix[downPosition.rowIndex + currentRowSpan] || [])[downPosition.columnIndex];
4840      if (targetCell) {
4841        cell.setAttribute('rowspan', currentRowSpan + Math.max(parseInt(targetCell.getAttribute('rowspan') || 1, 10) || 1, 1));
4842        if (!cell.innerHTML || cell.innerHTML === '&nbsp;') {
4843          cell.innerHTML = targetCell.innerHTML;
4844        } else if (targetCell.innerHTML && targetCell.innerHTML !== '&nbsp;') {
4845          cell.innerHTML += '<br>' + targetCell.innerHTML;
4846        }
4847        targetCell.remove();
4848      }
4849    }
4850  },
4851  splitTableCell: function (context) {
4852    var cell = context.cell;
4853    var row = context.row;
4854    if (!cell) {
4855      return;
4856    }
4857    var splitColSpan = Math.max(parseInt(cell.getAttribute('colspan') || 1, 10) || 1, 1);
4858    var splitRowSpan = Math.max(parseInt(cell.getAttribute('rowspan') || 1, 10) || 1, 1);
4859    var splitPosition = row && row.parentElement ? this.getCellPosition(row.parentElement, cell) : { rowIndex: row ? row.sectionRowIndex : 0, columnIndex: context.columnIndex };
4860    if (splitColSpan > 1) {
4861      cell.removeAttribute('colspan');
4862      for (var c = 1; c < splitColSpan; c++) {
4863        var splitCell = document.createElement(cell.tagName.toLowerCase());
4864        splitCell.innerHTML = '&nbsp;';
4865        cell.parentElement.insertBefore(splitCell, cell.parentElement.cells[(Array.prototype.indexOf.call(cell.parentElement.cells, cell) + c)] || null);
4866      }
4867    }
4868    if (splitRowSpan > 1 && row && row.parentElement) {
4869      cell.removeAttribute('rowspan');
4870      for (var sr = 1; sr < splitRowSpan; sr++) {
4871        var splitRow = row.parentElement.rows[row.sectionRowIndex + sr];
4872        if (splitRow) {
4873          var splitMatrix = this.getCellMatrix(row.parentElement);
4874          var splitDownCell = document.createElement(cell.tagName.toLowerCase());
4875          splitDownCell.innerHTML = '&nbsp;';
4876          var splitReference = null;
4877          for (var rc = splitPosition.columnIndex; rc < (splitMatrix[splitPosition.rowIndex + sr] || []).length; rc++) {
4878            if (splitMatrix[splitPosition.rowIndex + sr][rc] && splitMatrix[splitPosition.rowIndex + sr][rc] !== cell) {
4879              splitReference = splitMatrix[splitPosition.rowIndex + sr][rc];
4880              break;
4881            }
4882          }
4883          splitRow.insertBefore(splitDownCell, splitReference);
4884        }
4885      }
4886    }
4887  },
4888  execTableCommand: function (ref, name, value) {
4889    if (document.activeElement != ref && ref) {
4890      ref.focus();
4891    }
4892
4893    this.captureTableColumnHeaderFormat(ref, value);
4894
4895    try {
4896      if (name === 'insertTable') {
4897        return this.insertTable(ref, value);
4898      }
4899
4900      var context = (ref && ref.contextMenuTarget)
4901        ? this.getTableContextFromTarget(ref, ref.contextMenuTarget)
4902        : this.getTableContext(ref);
4903
4904      if (!context.table) {
4905        return this.createCommandResult(ref, false, 'table.action.requiresTable');
4906      }
4907
4908      switch (name) {
4909        case 'copyCells': return this.copyTableCells(ref);
4910        case 'pasteCells': return this.pasteTableCells(ref, context);
4911        case 'updateTable': this.updateTable(context, value); break;
4912        case 'addRowBefore':
4913        case 'addRowAfter': this.addTableRow(context, name); break;
4914        case 'deleteRow': this.deleteTableRow(context); break;
4915        case 'addColumnBefore':
4916        case 'addColumnAfter': this.addTableColumn(context, name); break;
4917        case 'deleteColumn': this.deleteTableColumn(context); break;
4918        case 'mergeCellRight':
4919          if (!this.mergeTableCellRight(ref, context)) {
4920            return this.createCommandResult(ref, false, 'table.merge.invalidSelection');
4921          }
4922          break;
4923        case 'mergeCellDown': this.mergeTableCellDown(context); break;
4924        case 'splitCell': this.splitTableCell(context); break;
4925        case 'deleteTable': this.deleteTable(context); break;
4926      }
4927
4928      return this.createCommandResult(ref, true);
4929    } finally {
4930      if (ref) {
4931        ref.contextMenuTarget = null;
4932      }
4933    }
4934  },
4935  insertTable: function (ref, value) {
4936    this.captureTableColumnHeaderFormat(ref, value);
4937    document.execCommand('insertHTML', false, this.createTableHTML(ref, value));
4938    return this.createCommandResult(ref, true);
4939  },
4940  captureTableColumnHeaderFormat: function (ref, value) {
4941    if (ref && value && typeof value.defaultColumnHeader === 'string' && value.defaultColumnHeader) {
4942      ref.tableColumnHeaderFormat = value.defaultColumnHeader;
4943    }
4944  },
4945  copyTableCells: function (ref) {
4946    return this.copySelectedTableCells(ref)
4947      ? this.createCommandResult(ref, true)
4948      : this.createCommandResult(ref, false, 'table.copy.invalidSelection');
4949  },
4950  pasteTableCells: function (ref, context) {
4951    return this.pasteSelectedTableCells(ref, context.cell)
4952      ? this.createCommandResult(ref, true)
4953      : this.createCommandResult(ref, false, 'table.paste.blocked');
4954  },
4955  deleteTable: function (context) {
4956    context.table.remove();
4957  },
4958  getEditorHtml: function (ref) {
4959    var selected = Array.from(ref.querySelectorAll('img.rz-state-selected'));
4960
4961    for (var img of selected) {
4962      img.classList.remove('rz-state-selected');
4963
4964      if (!img.getAttribute('class')) {
4965        img.removeAttribute('class');
4966      }
4967    }
4968
4969    var html = ref.innerHTML;
4970
4971    for (var img of selected) {
4972      img.classList.add('rz-state-selected');
4973    }
4974
4975    return html;
4976  },
4977  queryCommands: function (ref) {
4978    return {
4979      html: ref != null ? this.getEditorHtml(ref) : null,
4980      fontName: document.queryCommandValue('fontName'),
4981      fontSize: document.queryCommandValue('fontSize'),
4982      formatBlock: document.queryCommandValue('formatBlock'),
4983      bold: document.queryCommandState('bold'),
4984      underline: document.queryCommandState('underline'),
4985      justifyRight: document.queryCommandState('justifyRight'),
4986      justifyLeft: document.queryCommandState('justifyLeft'),
4987      justifyCenter: document.queryCommandState('justifyCenter'),
4988      justifyFull: document.queryCommandState('justifyFull'),
4989      italic: document.queryCommandState('italic'),
4990      strikeThrough: document.queryCommandState('strikeThrough'),
4991      superscript: document.queryCommandState('superscript'),
4992      subscript: document.queryCommandState('subscript'),
4993      unlink: document.queryCommandEnabled('unlink'),
4994      undo: document.queryCommandEnabled('undo'),
4995      redo: document.queryCommandEnabled('redo')
4996    };
4997  },
4998  mediaQueries: {},
4999  mediaQuery: function(query, instance) {
5000    if (instance) {
5001      function callback(event) {
5002          try { suppressDisposed(instance.invokeMethodAsync('OnChange', event.matches)); } catch { }
5003      };
5004      var query = matchMedia(query);
5005      this.mediaQueries[instance._id] = function() {
5006          query.removeListener(callback);
5007      }
5008      query.addListener(callback);
5009      return query.matches;
5010    } else {
5011        instance = query;
5012        if (this.mediaQueries[instance._id]) {
5013            this.mediaQueries[instance._id]();
5014            delete this.mediaQueries[instance._id];
5015        }
5016    }
5017  },
5018  createEditor: function (ref, uploadUrl, paste, instance, shortcuts) {
5019    function trackDrag(move, end, key) {
5020      document.addEventListener('mousemove', move);
5021      document.addEventListener('mouseup', end);
5022
5023      if (key) {
5024        document.addEventListener('keydown', key, true);
5025      }
5026
5027      return function () {
5028        document.removeEventListener('mousemove', move);
5029        document.removeEventListener('mouseup', end);
5030
5031        if (key) {
5032          document.removeEventListener('keydown', key, true);
5033        }
5034      };
5035    }
5036
5037    var imageHandleDirections = {
5038      nw: [-1, -1], n: [0, -1], ne: [1, -1],
5039      w: [-1, 0], e: [1, 0],
5040      sw: [-1, 1], s: [0, 1], se: [1, 1]
5041    };
5042    var minImageSize = 24;
5043
5044    ref.getSelectedImage = function () {
5045      return ref.querySelector('img.rz-state-selected');
5046    };
5047
5048    ref.getEditorContainer = function () {
5049      return ref.closest('.rz-html-editor');
5050    };
5051
5052    ref.removeImageHandles = function () {
5053      if (!ref.imageHandles) {
5054        return;
5055      }
5056
5057      window.removeEventListener('resize', ref.positionImageHandles);
5058      ref.imageHandlesClip.remove();
5059      ref.imageHandlesClip = null;
5060      ref.imageHandles = null;
5061      ref.imageSizeLabel = null;
5062      ref.imageSizeLabelSpace = 0;
5063    };
5064
5065    ref.positionImageHandles = function () {
5066      if (!ref.imageHandles) {
5067        return;
5068      }
5069
5070      var img = ref.getSelectedImage();
5071      var container = ref.getEditorContainer();
5072
5073      if (!img || !container || ref.hidden || !ref.isContentEditable) {
5074        ref.removeImageHandles();
5075        return;
5076      }
5077
5078      var containerRect = container.getBoundingClientRect();
5079      var contentRect = ref.getBoundingClientRect();
5080      var imgRect = img.getBoundingClientRect();
5081      var clipLeft = contentRect.left - containerRect.left - container.clientLeft;
5082      var clipTop = contentRect.top - containerRect.top - container.clientTop;
5083
5084      ref.imageHandlesClip.style.left = clipLeft + 'px';
5085      ref.imageHandlesClip.style.top = clipTop + 'px';
5086      ref.imageHandlesClip.style.width = contentRect.width + 'px';
5087      ref.imageHandlesClip.style.height = contentRect.height + 'px';
5088
5089      ref.imageHandles.style.left = (imgRect.left - contentRect.left) + 'px';
5090      ref.imageHandles.style.top = (imgRect.top - contentRect.top) + 'px';
5091      ref.imageHandles.style.width = imgRect.width + 'px';
5092      ref.imageHandles.style.height = imgRect.height + 'px';
5093
5094      var label = ref.imageSizeLabel;
5095
5096      if (label && !label.hidden) {
5097        label.textContent = Math.round(imgRect.width) + ' × ' + Math.round(imgRect.height);
5098        label.classList.toggle('rz-inside', imgRect.bottom + ref.imageSizeLabelSpace > contentRect.bottom);
5099      }
5100    };
5101
5102    ref.createImageHandles = function () {
5103      ref.removeImageHandles();
5104
5105      var img = ref.getSelectedImage();
5106      var container = ref.getEditorContainer();
5107
5108      if (!img || !container || !ref.isContentEditable) {
5109        return;
5110      }
5111
5112      var clip = document.createElement('div');
5113      clip.className = 'rz-html-editor-image-clip';
5114      clip.setAttribute('contenteditable', 'false');
5115
5116      var handles = document.createElement('div');
5117      handles.className = 'rz-html-editor-image-handles';
5118
5119      Object.keys(imageHandleDirections).forEach(function (position) {
5120        var handle = document.createElement('div');
5121        handle.className = 'rz-html-editor-image-handle rz-' + position;
5122        handle.dataset.position = position;
5123        handle.addEventListener('mousedown', ref.imageResizeStartListener);
5124        handles.appendChild(handle);
5125      });
5126
5127      ref.imageSizeLabel = document.createElement('div');
5128      ref.imageSizeLabel.className = 'rz-html-editor-image-size';
5129      ref.imageSizeLabel.hidden = true;
5130      handles.appendChild(ref.imageSizeLabel);
5131
5132      clip.appendChild(handles);
5133      container.appendChild(clip);
5134      ref.imageHandlesClip = clip;
5135      ref.imageHandles = handles;
5136
5137      window.addEventListener('resize', ref.positionImageHandles);
5138      ref.positionImageHandles();
5139    };
5140
5141    ref.showImageSizeLabel = function (visible) {
5142      var label = ref.imageSizeLabel;
5143
5144      if (!label) {
5145        return;
5146      }
5147
5148      label.hidden = !visible;
5149
5150      if (visible) {
5151        label.textContent = '0 × 0';
5152        ref.imageSizeLabelSpace = label.offsetHeight + parseFloat(getComputedStyle(label).marginTop || 0);
5153      }
5154    };
5155
5156    ref.imageResizeStartListener = function (e) {
5157      var img = ref.getSelectedImage();
5158
5159      if (!img || ref.imageResize) {
5160        return;
5161      }
5162
5163      e.preventDefault();
5164      e.stopPropagation();
5165
5166      ref.imageResize = {
5167        img: img,
5168        direction: imageHandleDirections[e.currentTarget.dataset.position],
5169        startX: e.clientX,
5170        startY: e.clientY,
5171        startWidth: img.offsetWidth,
5172        startHeight: img.offsetHeight,
5173        startAttributes: { width: img.getAttribute('width'), height: img.getAttribute('height') },
5174        startStyles: { width: img.style.width, height: img.style.height },
5175        ratio: img.offsetHeight ? img.offsetWidth / img.offsetHeight : 1
5176      };
5177
5178      img.style.width = '';
5179      img.style.height = '';
5180      img.setAttribute('width', ref.imageResize.startWidth);
5181      img.setAttribute('height', ref.imageResize.startHeight);
5182
5183      ref.releaseImageDrag = trackDrag(
5184        ref.documentImageResizeMoveListener,
5185        ref.documentImageResizeEndListener,
5186        ref.documentImageResizeKeyListener);
5187      ref.showImageSizeLabel(true);
5188      ref.positionImageHandles();
5189    };
5190
5191    ref.restoreImageSize = function (state) {
5192      Object.keys(state.startAttributes).forEach(function (name) {
5193        var value = state.startAttributes[name];
5194
5195        if (value !== null) {
5196          state.img.setAttribute(name, value);
5197        } else {
5198          state.img.removeAttribute(name);
5199        }
5200
5201        state.img.style[name] = state.startStyles[name];
5202      });
5203    };
5204
5205    ref.documentImageResizeKeyListener = function (e) {
5206      if (e.key !== 'Escape' || !ref.imageResize) {
5207        return;
5208      }
5209
5210      e.preventDefault();
5211      e.stopPropagation();
5212      ref.restoreImageSize(ref.imageResize);
5213      ref.stopImageDrag();
5214      ref.positionImageHandles();
5215    };
5216
5217    ref.documentImageResizeMoveListener = function (e) {
5218      var state = ref.imageResize;
5219
5220      if (!state || !ref.isConnected) {
5221        return;
5222      }
5223
5224      e.preventDefault();
5225
5226      var horizontal = state.direction[0];
5227      var vertical = state.direction[1];
5228      var width = state.startWidth;
5229      var height = state.startHeight;
5230
5231      if (horizontal) {
5232        width = Math.max(Math.round(state.startWidth + horizontal * (e.clientX - state.startX)), minImageSize);
5233      }
5234
5235      if (vertical && (!horizontal || e.shiftKey)) {
5236        height = Math.max(Math.round(state.startHeight + vertical * (e.clientY - state.startY)), minImageSize);
5237      } else if (horizontal && vertical) {
5238        height = Math.max(Math.round(width / state.ratio), minImageSize);
5239      }
5240
5241      state.img.setAttribute('width', width);
5242      state.img.setAttribute('height', height);
5243
5244      ref.positionImageHandles();
5245    };
5246
5247    ref.documentImageResizeEndListener = function () {
5248      var state = ref.imageResize;
5249
5250      if (!state) {
5251        return;
5252      }
5253
5254      var img = state.img;
5255      var width = img.getAttribute('width');
5256      var height = img.getAttribute('height');
5257
5258      var index = Array.prototype.indexOf.call(ref.querySelectorAll('img'), img);
5259
5260      ref.stopImageDrag();
5261      ref.restoreImageSize(state);
5262
5263      if (width === String(state.startWidth) && height === String(state.startHeight)) {
5264        ref.positionImageHandles();
5265        return;
5266      }
5267
5268      ref.deselectImage(img);
5269
5270      var target = img;
5271
5272      while (target.parentElement && target.parentElement !== ref && getComputedStyle(target.parentElement).display === 'inline') {
5273        target = target.parentElement;
5274      }
5275
5276      var replacement = target.cloneNode(true);
5277      var replacementImg = target === img
5278        ? replacement
5279        : replacement.querySelectorAll('img')[Array.prototype.indexOf.call(target.querySelectorAll('img'), img)];
5280
5281      replacementImg.setAttribute('width', width);
5282      replacementImg.setAttribute('height', height);
5283      replacementImg.style.width = '';
5284      replacementImg.style.height = '';
5285
5286      if (!replacementImg.style.length) {
5287        replacementImg.removeAttribute('style');
5288      }
5289
5290      var range = document.createRange();
5291      range.selectNode(target);
5292      var selection = getSelection();
5293      selection.removeAllRanges();
5294      selection.addRange(range);
5295      ref.focus();
5296      document.execCommand('insertHTML', false, replacement.outerHTML);
5297
5298      ref.selectImage(ref.querySelectorAll('img')[index]);
5299    };
5300
5301    ref.deselectImage = function (img) {
5302      img.classList.remove('rz-state-selected');
5303
5304      if (!img.getAttribute('class')) {
5305        img.removeAttribute('class');
5306      }
5307
5308      ref.removeImageHandles();
5309    };
5310
5311    ref.selectImage = function (img) {
5312      if (!img) {
5313        ref.removeImageHandles();
5314        return;
5315      }
5316
5317      img.classList.add('rz-state-selected');
5318
5319      var range = document.createRange();
5320      range.selectNode(img);
5321      var selection = getSelection();
5322      selection.removeAllRanges();
5323      selection.addRange(range);
5324
5325      ref.createImageHandles();
5326    };
5327
5328    ref.imageHandlesObserver = new MutationObserver(ref.positionImageHandles);
5329
5330    ref.stopImageDrag = function () {
5331      ref.imageResize = null;
5332      ref.showImageSizeLabel(false);
5333
5334      if (ref.releaseImageDrag) {
5335        ref.releaseImageDrag();
5336        ref.releaseImageDrag = null;
5337      }
5338    };
5339
5340    ref.inputListener = function () {
5341      ref.positionImageHandles();
5342      try { suppressDisposed(instance.invokeMethodAsync('OnChange', Radzen.getEditorHtml(ref))); } catch { }
5343    };
5344    ref.keydownListener = function (e) {
5345      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'c') {
5346        var currentCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5347        if (currentCell && ref.contains(currentCell)) {
5348          if (!currentCell.classList.contains('rz-html-editor-cell-selected')) {
5349            Radzen.clearSelectedTableCells(ref);
5350            currentCell.classList.add('rz-html-editor-cell-selected');
5351          }
5352          e.preventDefault();
5353          Radzen.copySelectedTableCells(ref);
5354          return;
5355        }
5356      }
5357
5358      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'v') {
5359        var pasteCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5360        if (pasteCell && ref.contains(pasteCell) && ref.copiedCells && ref.copiedCells.matrix) {
5361          e.preventDefault();
5362          Radzen.pasteSelectedTableCells(ref, pasteCell);
5363          try { instance.invokeMethodAsync('OnChange', ref.innerHTML); } catch { }
5364          return;
5365        }
5366      }
5367
5368      if (e.key === 'Tab') {
5369        var activeCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5370        if (activeCell && ref.contains(activeCell)) {
5371          e.preventDefault();
5372          var activeRow = activeCell.parentElement;
5373          var section = activeRow ? activeRow.parentElement : null;
5374          var matrix = Radzen.getCellMatrix(section);
5375          var activePosition = Radzen.getCellPosition(section, activeCell, matrix);
5376          var rowIndex = activePosition.rowIndex;
5377          var columnIndex = activePosition.columnIndex;
5378          var colSpan = Math.max(parseInt(activeCell.getAttribute('colspan') || 1, 10) || 1, 1);
5379          var rowSpan = Math.max(parseInt(activeCell.getAttribute('rowspan') || 1, 10) || 1, 1);
5380          var nextColumnIndex = e.shiftKey ? columnIndex - 1 : columnIndex + colSpan;
5381          var nextRowIndex = rowIndex;
5382
5383          if (nextColumnIndex < 0) {
5384            nextRowIndex = Math.max(rowIndex - 1, 0);
5385            nextColumnIndex = Math.max((matrix[nextRowIndex] || []).length - 1, 0);
5386          }
5387
5388          while (matrix[nextRowIndex] && !matrix[nextRowIndex][nextColumnIndex] && nextColumnIndex >= 0) {
5389            nextColumnIndex += e.shiftKey ? -1 : 1;
5390          }
5391
5392          if ((!matrix[nextRowIndex] || !matrix[nextRowIndex][nextColumnIndex]) && !e.shiftKey) {
5393            nextRowIndex = rowIndex + rowSpan;
5394            nextColumnIndex = 0;
5395          }
5396
5397          var nextCell = (matrix[nextRowIndex] || [])[nextColumnIndex];
5398          if (!nextCell && !e.shiftKey && section && activeRow && activeRow.parentElement && activeRow.parentElement.tagName === 'TBODY') {
5399            var table = activeCell.closest('table');
5400            if (table) {
5401              Radzen.execTableCommand(ref, 'addRowAfter');
5402              matrix = Radzen.getCellMatrix(section);
5403              nextRowIndex = Math.min(rowIndex + rowSpan, matrix.length - 1);
5404              nextCell = (matrix[nextRowIndex] || [])[0];
5405            }
5406          }
5407
5408          if (nextCell) {
5409            var range = document.createRange();
5410            range.selectNodeContents(nextCell);
5411            range.collapse(true);
5412            var selection = getSelection();
5413            selection.removeAllRanges();
5414            selection.addRange(range);
5415            ref.focus();
5416          }
5417          return;
5418        }
5419      }
5420
5421      var key = '';
5422      if (e.ctrlKey || e.metaKey) {
5423        key += 'Ctrl+';
5424      }
5425      if (e.altKey) {
5426        key += 'Alt+';
5427      }
5428      if (e.shiftKey) {
5429        key += 'Shift+';
5430      }
5431      key += e.code.replace('Key', '').replace('Digit', '').replace('Numpad', '');
5432
5433      if (shortcuts.includes(key)) {
5434        e.preventDefault();
5435        try { suppressDisposed(instance.invokeMethodAsync('ExecuteShortcutAsync', key)); } catch { }
5436      }
5437    };
5438
5439    ref.clickListener = function (e) {
5440      if (e.target) {
5441        if (e.target.matches('a,button')) {
5442          e.preventDefault();
5443        }
5444
5445        for (var img of ref.querySelectorAll('img.rz-state-selected')) {
5446          ref.deselectImage(img);
5447        }
5448
5449        if (e.target.matches('img')) {
5450          ref.selectImage(e.target);
5451        } else {
5452          var clickedCell = e.target.closest && e.target.closest('td,th');
5453          if (clickedCell && ref.contains(clickedCell)) {
5454            if (e.shiftKey && ref.lastSelectedTableCell) {
5455              Radzen.selectTableCellRange(ref, ref.lastSelectedTableCell, clickedCell);
5456            } else if (e.ctrlKey || e.metaKey) {
5457              clickedCell.classList.toggle('rz-html-editor-cell-selected');
5458            } else {
5459              Radzen.clearSelectedTableCells(ref);
5460              clickedCell.classList.add('rz-html-editor-cell-selected');
5461            }
5462            ref.lastSelectedTableCell = clickedCell;
5463          } else {
5464            Radzen.clearSelectedTableCells(ref);
5465            ref.lastSelectedTableCell = null;
5466          }
5467        }
5468      }
5469    }
5470
5471    ref.contextMenuListener = function (e) {
5472      var cell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5473      if (cell && ref.contains(cell)) {
5474        if (!cell.classList.contains('rz-html-editor-cell-selected')) {
5475          Radzen.clearSelectedTableCells(ref);
5476          cell.classList.add('rz-html-editor-cell-selected');
5477        }
5478        ref.lastSelectedTableCell = cell;
5479        ref.contextMenuTarget = cell;
5480        e.preventDefault();
5481        try { instance.invokeMethodAsync('OnContextMenu', e.clientX, e.clientY); } catch { }
5482      }
5483    }
5484
5485    ref.mousemoveListener = function (e) {
5486      var hoveredCell = e.target && e.target.closest ? e.target.closest('td,th') : null;
5487      if (!hoveredCell || !ref.contains(hoveredCell)) {
5488        ref.style.cursor = '';
5489        return;
5490      }
5491
5492      var rect = hoveredCell.getBoundingClientRect();
5493      var nearRightEdge = Math.abs(rect.right - e.clientX) < 6;
5494      if (nearRightEdge) {
5495        ref.style.cursor = 'col-resize';
5496        ref.resizeTargetCell = hoveredCell;
5497      } else if (!ref.isResizingColumn) {
5498        ref.style.cursor = '';
5499        ref.resizeTargetCell = null;
5500      }
5501    };
5502
5503    ref.mousedownResizeListener = function (e) {
5504      if (ref.resizeTargetCell && ref.style.cursor === 'col-resize') {
5505        e.preventDefault();
5506        ref.isResizingColumn = true;
5507        ref.resizeStartX = e.clientX;
5508        ref.resizeStartWidth = ref.resizeTargetCell.getBoundingClientRect().width;
5509        ref.releaseColumnDrag = trackDrag(ref.documentMouseMoveResizeListener, ref.mouseupResizeListener);
5510      }
5511    };
5512
5513    ref.mouseupResizeListener = function () {
5514      if (ref.isResizingColumn) {
5515        ref.isResizingColumn = false;
5516        ref.style.cursor = '';
5517        ref.releaseColumnDrag();
5518        ref.releaseColumnDrag = null;
5519        ref.inputListener();
5520      }
5521    };
5522
5523    ref.documentMouseMoveResizeListener = function (e) {
5524      if (ref.isResizingColumn && ref.resizeTargetCell && ref.isConnected) {
5525        var width = Math.max(ref.resizeStartWidth + (e.clientX - ref.resizeStartX), 24);
5526        var resizeSection = ref.resizeTargetCell.parentElement ? ref.resizeTargetCell.parentElement.parentElement : null;
5527        var resizeMatrix = Radzen.getCellMatrix(resizeSection);
5528        var resizePosition = Radzen.getCellPosition(resizeSection, ref.resizeTargetCell, resizeMatrix);
5529        var resizeColumnIndex = resizePosition.columnIndex;
5530        for (var resizeRowIndex = 0; resizeRowIndex < resizeMatrix.length; resizeRowIndex++) {
5531          var resizeCell = resizeMatrix[resizeRowIndex][resizeColumnIndex];
5532          if (resizeCell) {
5533            resizeCell.style.width = width + 'px';
5534          }
5535        }
5536      }
5537    };
5538
5539    ref.selectionChangeListener = function () {
5540      if (document.activeElement == ref) {
5541        var selectedImage = ref.getSelectedImage();
5542
5543        if (selectedImage && !ref.imageResize && !getSelection().containsNode(selectedImage)) {
5544          ref.deselectImage(selectedImage);
5545        }
5546
5547        try { suppressDisposed(instance.invokeMethodAsync('OnSelectionChange')); } catch { }
5548      }
5549    };
5550    ref.handleInsert = function (e, transfer, hasDelegate) {
5551      var hasFiles = transfer.files.length > 0;
5552      var hasHtml = transfer.types.indexOf('text/html') > -1;
5553      var hasText = transfer.types.indexOf('text/plain') > -1;
5554
5555      if (hasFiles && !hasHtml && !hasText) {
5556        for (const file of transfer.files) {
5557          ref.handleFileInsert(e, file, hasDelegate);
5558        }
5559      }
5560      else if (hasDelegate) {
5561        ref.handleTextInsert(e, transfer);
5562      }
5563    };
5564    ref.handleFileInsert = function (event, file, hasDelegate) {
5565      event.preventDefault();
5566
5567      if (uploadUrl) {
5568        var xhr = new XMLHttpRequest();
5569        var data = new FormData();
5570        data.append("file", file);
5571        xhr.onreadystatechange = function (e) {
5572          if (xhr.readyState === XMLHttpRequest.DONE) {
5573            var status = xhr.status;
5574            if (status === 0 || (status >= 200 && status < 400)) {
5575              var result = JSON.parse(xhr.responseText);
5576              var html = '<img src="' + result.url + '">';
5577              if (hasDelegate) {
5578                try { instance.invokeMethodAsync('OnPaste', html)
5579                  .then(function (html) {
5580                    document.execCommand("insertHTML", false, html);
5581                  }); } catch { }
5582              } else {
5583                document.execCommand("insertHTML", false, '<img src="' + result.url + '">');
5584              }
5585              try { suppressDisposed(instance.invokeMethodAsync('OnUploadComplete', xhr.responseText)); } catch { }
5586            } else {
5587              try { suppressDisposed(instance.invokeMethodAsync('OnError', xhr.responseText)); } catch { }
5588            }
5589          }
5590        }
5591        try { instance.invokeMethodAsync('GetHeaders').then(function (headers) {
5592          xhr.open('POST', uploadUrl, true);
5593          for (var name in headers) {
5594            xhr.setRequestHeader(name, headers[name]);
5595          }
5596          xhr.send(data);
5597        }); } catch { }
5598      } else {
5599        var reader = new FileReader();
5600        reader.onload = function (e) {
5601          var html = '<img src="' + e.target.result + '">';
5602
5603          if (hasDelegate) {
5604            try { instance.invokeMethodAsync('OnPaste', html)
5605              .then(function (html) {
5606                document.execCommand("insertHTML", false, html);
5607              }); } catch { }
5608            } else {
5609              document.execCommand("insertHTML", false, html);
5610            }
5611        };
5612        reader.readAsDataURL(file);
5613      }
5614    }
5615    ref.handleTextInsert = function (e, transfer) {
5616      e.preventDefault();
5617
5618      var data = transfer.getData('text/html') || transfer.getData('text/plain');
5619
5620      const startMarker = "<!--StartFragment-->";
5621      const endMarker = "<!--EndFragment-->";
5622
5623      const startIndex = data.indexOf(startMarker);
5624      const endIndex = data.indexOf(endMarker);
5625
5626      // check if the pasted data contains fragment markers
5627      if (startIndex != -1 || endIndex != -1 || endIndex > startIndex) {
5628        // only paste the fragment
5629        data = data.substring(startIndex + startMarker.length, endIndex).trim();
5630      }
5631
5632      try { instance.invokeMethodAsync('OnPaste', data)
5633        .then(ref.focus())
5634        .then(function (html) {
5635          document.execCommand("insertHTML", false, html);
5636        }); } catch { }
5637    }
5638    ref.pasteListener = function (e) {
5639      ref.handleInsert(e, e.clipboardData, paste);
5640    };
5641    ref.dropListener = function (e) {
5642      ref.handleInsert(e, e.dataTransfer, paste);
5643    };
5644    ref.addEventListener('input', ref.inputListener);
5645    ref.addEventListener('paste', ref.pasteListener);
5646    ref.addEventListener('drop', ref.dropListener);
5647    ref.addEventListener('keydown', ref.keydownListener);
5648    ref.addEventListener('click', ref.clickListener);
5649    ref.addEventListener('contextmenu', ref.contextMenuListener);
5650    ref.addEventListener('mousemove', ref.mousemoveListener);
5651    ref.addEventListener('mousedown', ref.mousedownResizeListener);
5652    ref.addEventListener('scroll', ref.positionImageHandles);
5653    ref.imageHandlesObserver.observe(ref, { attributes: true, attributeFilter: ['hidden', 'contenteditable'], childList: true });
5654    document.addEventListener('selectionchange', ref.selectionChangeListener);
5655    document.execCommand('styleWithCSS', false, true);
5656    return {
5657      dispose: function () {
5658        if (ref) {
5659          ref.removeEventListener('input', ref.inputListener);
5660          ref.removeEventListener('paste', ref.pasteListener);
5661          ref.removeEventListener('drop', ref.dropListener);
5662          ref.removeEventListener('keydown', ref.keydownListener);
5663          ref.removeEventListener('click', ref.clickListener);
5664          ref.removeEventListener('contextmenu', ref.contextMenuListener);
5665          ref.removeEventListener('mousemove', ref.mousemoveListener);
5666          ref.removeEventListener('mousedown', ref.mousedownResizeListener);
5667          ref.removeEventListener('scroll', ref.positionImageHandles);
5668          ref.imageHandlesObserver.disconnect();
5669          ref.stopImageDrag();
5670          ref.removeImageHandles();
5671          ref.isResizingColumn = false;
5672
5673          if (ref.releaseColumnDrag) {
5674            ref.releaseColumnDrag();
5675            ref.releaseColumnDrag = null;
5676          }
5677          document.removeEventListener('selectionchange', ref.selectionChangeListener);
5678        }
5679      }
5680    };
5681  },
5682  saveSelection: function (ref) {
5683    if (!ref) return;
5684    var selection = getSelection();
5685    if (selection.rangeCount > 0) {
5686      var range = selection.getRangeAt(0);
5687      if (ref.contains(range.commonAncestorContainer)) {
5688        ref.range = range;
5689      }
5690    }
5691  },
5692  restoreSelection: function (ref) {
5693    if (!ref) return;
5694    var range = ref.range;
5695    if (range) {
5696      delete ref.range;
5697      if(ref) {
5698          ref.focus();
5699      }
5700      var selection = getSelection();
5701      selection.removeAllRanges();
5702      selection.addRange(range);
5703    }
5704  },
5705  selectionAttributes: function (selector, attributes, container) {
5706    var selection = getSelection();
5707    var range = selection.rangeCount > 0 && selection.getRangeAt(0);
5708    var parent = range && range.commonAncestorContainer;
5709    var img = container.querySelector('img.rz-state-selected');
5710    var inside = img && selector == 'img';
5711    while (parent) {
5712      if (parent == container) {
5713        inside = true;
5714        break;
5715      }
5716      parent = parent.parentNode;
5717    }
5718    if (!inside) {
5719      return {};
5720    }
5721    var target = selection.focusNode;
5722    var innerHTML;
5723
5724    if (img && selector == 'img') {
5725      target = img;
5726    } else if (target) {
5727      if (target.nodeType == 3) {
5728        target = target.parentElement;
5729      } else {
5730        if (img) {
5731          target = target.childNodes[range.startOffset]
5732        } else {
5733          target = target.childNodes[selection.focusOffset];
5734        }
5735        if (target) {
5736          innerHTML = target.outerHTML;
5737        }
5738      }
5739      if (target && target.matches && !target.matches(selector)) {
5740        target = target.closest(selector);
5741      }
5742    }
5743
5744    return attributes.reduce(function (result, name) {
5745      if (target) {
5746        result[name] = name == 'innerText' ? target[name] : target.getAttribute(name);
5747      }
5748      return result;
5749    }, { innerText: selection.toString(), innerHTML: innerHTML });
5750  },
5751  startDrag: function (ref, instance, handler) {
5752    if (!ref) {
5753        return { left: 0, top: 0, width: 0, height: 0 };
5754    }
5755    Radzen.endDrag(ref);
5756    ref.mouseMoveHandler = function (e) {
5757      try { suppressDisposed(instance.invokeMethodAsync(handler, { clientX: e.clientX, clientY: e.clientY })); } catch { }
5758    };
5759    ref.touchMoveHandler = function (e) {
5760      if (e.targetTouches[0] && ref.contains(e.targetTouches[0].target)) {
5761        try { suppressDisposed(instance.invokeMethodAsync(handler, { clientX: e.targetTouches[0].clientX, clientY: e.targetTouches[0].clientY })); } catch { }
5762      }
5763    };
5764    ref.mouseUpHandler = function (e) {
5765      Radzen.endDrag(ref);
5766    };
5767    ref.dragStartHandler = function (e) {
5768      e.preventDefault();
5769    };
5770    document.addEventListener('mousemove', ref.mouseMoveHandler);
5771    document.addEventListener('mouseup', ref.mouseUpHandler);
5772    document.addEventListener('touchmove', ref.touchMoveHandler, { passive: true, capture: true })
5773    document.addEventListener('touchend', ref.mouseUpHandler, { passive: true });
5774    document.addEventListener('dragstart', ref.dragStartHandler);
5775    document.addEventListener('dragend', ref.mouseUpHandler);
5776    return Radzen.clientRect(ref);
5777  },
5778  submit: function (form) {
5779    form.submit();
5780  },
5781  clientRect: function (arg) {
5782    var el = arg instanceof Element || arg instanceof HTMLDocument
5783        ? arg
5784        : document.getElementById(arg);
5785    var rect = el.getBoundingClientRect();
5786    return { left: rect.left, top: rect.top, width: rect.width, height: rect.height };
5787  },
5788  outerHTML: function (arg) {
5789    var el = arg instanceof Element || arg instanceof HTMLDocument
5790        ? arg
5791        : document.getElementById(arg);
5792    return el ? el.outerHTML : '';
5793  },
5794  endDrag: function (ref) {
5795    document.removeEventListener('mousemove', ref.mouseMoveHandler);
5796    document.removeEventListener('mouseup', ref.mouseUpHandler);
5797    document.removeEventListener('touchmove', ref.touchMoveHandler)
5798    document.removeEventListener('touchend', ref.mouseUpHandler);
5799    document.removeEventListener('dragstart', ref.dragStartHandler);
5800    document.removeEventListener('dragend', ref.mouseUpHandler);
5801  },
5802  capturePointer: function (el, pointerId) {
5803    try {
5804      if (el && el.setPointerCapture) {
5805        el.setPointerCapture(pointerId);
5806      }
5807    } catch (e) { /* ignore */ }
5808  },
5809  releasePointer: function (el, pointerId) {
5810    try {
5811      if (el && el.releasePointerCapture && (!el.hasPointerCapture || el.hasPointerCapture(pointerId))) {
5812        el.releasePointerCapture(pointerId);
5813      }
5814    } catch (e) { /* ignore */ }
5815  },
5816  startColumnReorder: function(id, gridId, gridRef) {
5817      if (Radzen[id + 'reorderCleanup']) {
5818          Radzen[id + 'reorderCleanup']();
5819      }
5820
5821      var grid = document.getElementById(gridId);
5822      var el = document.getElementById(id + '-drag');
5823      var cell = el.parentNode.parentNode;
5824      var visual = document.createElement("th");
5825      visual.className = cell.className + ' rz-column-draggable';
5826      visual.style = cell.style;
5827      visual.style.display = 'none';
5828      visual.style.position = 'absolute';
5829      visual.style.height = cell.offsetHeight + 'px';
5830      visual.style.width = cell.offsetWidth + 'px';
5831      visual.style.zIndex = 2000;
5832      visual.style.pointerEvents = 'none';
5833      visual.innerHTML = cell.firstChild.outerHTML;
5834      visual.id = id + 'visual';
5835      document.body.appendChild(visual);
5836
5837      var resizers = cell.parentNode.querySelectorAll('.rz-column-resizer');
5838      for (let i = 0; i < resizers.length; i++) {
5839          resizers[i].style.display = 'none';
5840      }
5841
5842      var lastTouchX = null;
5843      var lastTouchY = null;
5844      var scroller = grid.querySelector('.rz-data-grid-data');
5845      var autoScrollSpeed = 0;
5846      var autoScrollFrame = null;
5847
5848      var stopAutoScroll = function () {
5849          if (autoScrollFrame) {
5850              cancelAnimationFrame(autoScrollFrame);
5851          }
5852          autoScrollFrame = null;
5853          autoScrollSpeed = 0;
5854      };
5855
5856      var autoScrollStep = function () {
5857          autoScrollFrame = null;
5858          if (!autoScrollSpeed || !scroller) {
5859              return;
5860          }
5861          var before = scroller.scrollLeft;
5862          scroller.scrollLeft = before + autoScrollSpeed;
5863          if (Math.abs(scroller.scrollLeft - before) >= 0.5) {
5864              autoScrollFrame = requestAnimationFrame(autoScrollStep);
5865          }
5866      };
5867
5868      var updateAutoScroll = function (clientX, clientY) {
5869          if (!scroller || scroller.scrollWidth <= scroller.clientWidth) {
5870              return;
5871          }
5872          var gridRect = grid.getBoundingClientRect();
5873          var speed = 0;
5874          if (clientY >= gridRect.top && clientY <= gridRect.bottom) {
5875              var rect = scroller.getBoundingClientRect();
5876              var left = rect.left;
5877              var right = rect.right;
5878              var middle = (rect.left + rect.right) / 2;
5879              var frozen = scroller.querySelectorAll('thead th.rz-frozen-cell');
5880              for (var i = 0; i < frozen.length; i++) {
5881                  var frozenRect = frozen[i].getBoundingClientRect();
5882                  if (frozenRect.width === 0) {
5883                      continue;
5884                  }
5885                  if ((frozenRect.left + frozenRect.right) / 2 < middle) {
5886                      left = Math.max(left, frozenRect.right);
5887                  } else {
5888                      right = Math.min(right, frozenRect.left);
5889                  }
5890              }
5891              var visible = right - left;
5892              if (visible > 0) {
5893                  var zone = Math.max(24, Math.min(80, visible * 0.15));
5894                  var depth = 0;
5895                  if (clientX < left + zone) {
5896                      depth = -(left + zone - clientX);
5897                  } else if (clientX > right - zone) {
5898                      depth = clientX - (right - zone);
5899                  }
5900                  if (depth) {
5901                      var ratio = Math.min(1, Math.abs(depth) / zone);
5902                      speed = Math.sign(depth) * Math.ceil(2 + 18 * ratio * ratio);
5903                  }
5904              }
5905          }
5906          autoScrollSpeed = speed;
5907          if (speed && !autoScrollFrame) {
5908              autoScrollFrame = requestAnimationFrame(autoScrollStep);
5909          }
5910      };
5911
5912      var cleanup = function () {
5913          stopAutoScroll();
5914
5915          var ghost = document.getElementById(id + 'visual');
5916          if (ghost && ghost.parentNode) {
5917              ghost.parentNode.removeChild(ghost);
5918          }
5919          var resizers = cell.parentNode ? cell.parentNode.querySelectorAll('.rz-column-resizer') : [];
5920          for (let i = 0; i < resizers.length; i++) {
5921              resizers[i].style.display = 'block';
5922          }
5923
5924          document.removeEventListener('mousemove', move);
5925          grid.removeEventListener('touchmove', touchmove);
5926          grid.removeEventListener('click', end);
5927          document.removeEventListener('mouseup', end);
5928          document.removeEventListener('touchend', end);
5929          document.removeEventListener('touchcancel', end);
5930
5931          if (Radzen[id + 'reorderCleanup'] === cleanup) {
5932              Radzen[id + 'reorderCleanup'] = null;
5933          }
5934      };
5935
5936      var end = function (e) {
5937          var triggeredByTouch = e && (e.type === 'touchend' || e.type === 'touchcancel');
5938          var touchX = lastTouchX;
5939          var touchY = lastTouchY;
5940          if (triggeredByTouch && e.changedTouches && e.changedTouches[0]) {
5941              touchX = e.changedTouches[0].clientX;
5942              touchY = e.changedTouches[0].clientY;
5943          }
5944
5945          cleanup();
5946
5947          if (triggeredByTouch && gridRef && touchX != null && touchY != null) {
5948              var target = document.elementFromPoint(touchX, touchY);
5949              if (target && grid.contains(target)) {
5950                  var groupHeader = target.closest('.rz-group-header');
5951                  if (groupHeader && grid.contains(groupHeader)) {
5952                      try { gridRef.invokeMethodAsync('RadzenGrid.OnColumnDropToGroup'); } catch { }
5953                      return;
5954                  }
5955                  var th = target.closest('th[data-column-index]');
5956                  if (th && grid.contains(th)) {
5957                      var columnIndex = parseInt(th.getAttribute('data-column-index'), 10);
5958                      if (!isNaN(columnIndex)) {
5959                          try { gridRef.invokeMethodAsync('RadzenGrid.OnColumnReorderEnded', columnIndex); } catch { }
5960                      }
5961                  }
5962              }
5963          }
5964      };
5965
5966      var move = function (e) {
5967          if (e.buttons === 0 || !grid.isConnected) {
5968              cleanup();
5969              return;
5970          }
5971
5972          var ghost = document.getElementById(id + 'visual');
5973          if (ghost) {
5974              ghost.style.display = 'block';
5975
5976              if (/Edge/.test(navigator.userAgent)) {
5977                  var scrollLeft = document.body.scrollLeft;
5978                  var scrollTop = document.body.scrollTop;
5979              } else {
5980                  var scrollLeft = document.documentElement.scrollLeft;
5981                  var scrollTop = document.documentElement.scrollTop;
5982              }
5983
5984              ghost.style.top = e.clientY + scrollTop + 10 + 'px';
5985              ghost.style.left = e.clientX + scrollLeft + 10 + 'px';
5986          }
5987          updateAutoScroll(e.clientX, e.clientY);
5988      };
5989
5990      var touchmove = function (e) {
5991          if (e.touches && e.touches[0]) {
5992              lastTouchX = e.touches[0].clientX;
5993              lastTouchY = e.touches[0].clientY;
5994              move({ clientX: e.touches[0].clientX, clientY: e.touches[0].clientY });
5995              if (e.cancelable) {
5996                  e.preventDefault();
5997              }
5998          }
5999      };
6000
6001      Radzen[id + 'reorderCleanup'] = cleanup;
6002
6003      grid.addEventListener('click', end);
6004      document.addEventListener('mouseup', end);
6005      document.addEventListener('touchend', end, { passive: true });
6006      document.addEventListener('touchcancel', end, { passive: true });
6007      document.addEventListener('mousemove', move);
6008      grid.addEventListener('touchmove', touchmove, { passive: false });
6009  },
6010  stopColumnResize: function (id, grid, columnIndex) {
6011    var resize = Radzen[id + 'columnResize'];
6012    if (resize) {
6013        resize.mouseUpHandler();
6014    }
6015  },
6016  // A grid rendered inside another grid's row-detail template is a descendant of it, so every lookup
6017  // here is scoped to this grid's own table and to a row's own cells. Descendant queries reach the
6018  // inner grid and pin its cells to the outer grid's offsets.
6019  frozenCellsOf: function(row, side) {
6020      var found = [];
6021      if (!row) return found;
6022      var prefix = 'rz-frozen-cell-' + side;
6023      for (var i = 0; i < row.children.length; i++) {
6024          var cell = row.children[i];
6025          if (cell.classList.contains(prefix)
6026              || cell.classList.contains(prefix + '-end')
6027              || cell.classList.contains(prefix + '-inner')) {
6028              found.push(cell);
6029          }
6030      }
6031      return found;
6032  },
6033  updateFrozenColumnPositions: function(gridElement) {
6034      if (!gridElement) return;
6035      // This grid's own table. querySelector returns the first in document order, which is this
6036      // grid's rather than one nested inside it.
6037      var table = gridElement.querySelector('.rz-grid-table');
6038      if (!table) return;
6039      // Get frozen cell positions from the header row first, then apply to all rows
6040      var headerRow = table.querySelector(':scope > thead > tr');
6041      if (!headerRow) return;
6042      var leftHeaderCells = Radzen.frozenCellsOf(headerRow, 'left');
6043      var rightHeaderCells = Radzen.frozenCellsOf(headerRow, 'right');
6044      // Calculate offsets from header widths
6045      var leftOffsets = [];
6046      var offset = 0;
6047      for (var i = 0; i < leftHeaderCells.length; i++) {
6048          leftOffsets.push(offset);
6049          offset += leftHeaderCells[i].getBoundingClientRect().width;
6050      }
6051      var rightOffsets = [];
6052      offset = 0;
6053      for (var i = rightHeaderCells.length - 1; i >= 0; i--) {
6054          rightOffsets[i] = offset;
6055          offset += rightHeaderCells[i].getBoundingClientRect().width;
6056      }
6057      // Apply offsets to this grid's own rows
6058      var rows = table.querySelectorAll(':scope > thead > tr, :scope > tbody > tr, :scope > tfoot > tr');
6059      for (var r = 0; r < rows.length; r++) {
6060          var row = rows[r];
6061          var leftCells = Radzen.frozenCellsOf(row, 'left');
6062          for (var i = 0; i < leftCells.length && i < leftOffsets.length; i++) {
6063              leftCells[i].style.setProperty('inset-inline-start', leftOffsets[i] + 'px');
6064          }
6065          var rightCells = Radzen.frozenCellsOf(row, 'right');
6066          for (var i = 0; i < rightCells.length && i < rightOffsets.length; i++) {
6067              rightCells[i].style.setProperty('inset-inline-end', rightOffsets[i] + 'px');
6068          }
6069      }
6070  },
6071  startColumnResize: function(id, grid, columnIndex, clientX) {
6072      var el = document.getElementById(id + '-resizer');
6073      var resizeKey = id + 'columnResize';
6074      var cell = el.parentNode.parentNode;
6075      var col = document.getElementById(id + '-col');
6076      var gridElement = cell.closest('.rz-data-grid');
6077      var hasFrozenColumns = gridElement != null && gridElement.querySelector('.rz-frozen-cell') != null;
6078      var table = cell.closest('.rz-grid-table');
6079      var colgroup = table ? table.querySelector(':scope > colgroup') : null;
6080      var allColumns = colgroup ? Array.from(colgroup.children) : [];
6081      var headerCells = cell.parentNode.children.length === allColumns.length
6082          ? Array.from(cell.parentNode.children)
6083          : [];
6084      var pinned = allColumns.map(function (column) {
6085          return column === col || !!column.style.width || !column.id.endsWith('-col');
6086      });
6087      var columns = [];
6088      var bounds = [];
6089      allColumns.forEach(function (column, index) {
6090          if (!column.id.endsWith('-col')) return;
6091          var style = getComputedStyle(headerCells[index] || column);
6092          columns.push(column);
6093          bounds.push({
6094              min: parseFloat(style.minWidth) || 0,
6095              max: parseFloat(style.maxWidth),
6096              pinned: pinned[index]
6097          });
6098      });
6099      var cellStyle = getComputedStyle(cell);
6100      var minWidth = parseFloat(cellStyle.minWidth) || 0;
6101      var maxWidth = parseFloat(cellStyle.maxWidth);
6102      var declaredWidths = allColumns.map(column => column.style.width);
6103      var columnWidths = allColumns.map(column => column.getBoundingClientRect().width);
6104      allColumns.forEach(function (column, index) {
6105          if (pinned[index]) {
6106              column.style.width = columnWidths[index] + 'px';
6107          }
6108      });
6109      Radzen[resizeKey] = {
6110          clientX: clientX,
6111          width: cell.getBoundingClientRect().width,
6112          resized: false,
6113          mouseUpHandler: function (e) {
6114              var resize = Radzen[resizeKey];
6115
6116              if (!resize) return;
6117
6118              if (!resize.resized) {
6119                  allColumns.forEach(function (column, index) {
6120                      column.style.width = declaredWidths[index];
6121                  });
6122              }
6123              else {
6124                  var widths = columns.map(function (column, index) {
6125                      if (!bounds[index].pinned) {
6126                          return 0;
6127                      }
6128
6129                      var width = Math.max(parseFloat(column.style.width), bounds[index].min);
6130
6131                      return Number.isNaN(bounds[index].max) ? width : Math.min(width, bounds[index].max);
6132                  });
6133
6134                  try {
6135                      if (columns.length) {
6136                          grid.invokeMethodAsync('RadzenGrid.OnColumnsResized', columnIndex, widths[columnIndex], widths);
6137                      } else {
6138                          grid.invokeMethodAsync('RadzenGrid.OnColumnResized', columnIndex, cell.getBoundingClientRect().width);
6139                      }
6140                  } catch { }
6141              }
6142
6143              el.style.width = null;
6144              document.removeEventListener('mousemove', resize.mouseMoveHandler);
6145              document.removeEventListener('mouseup', resize.mouseUpHandler);
6146              document.removeEventListener('touchmove', resize.touchMoveHandler)
6147              document.removeEventListener('touchend', resize.mouseUpHandler);
6148              Radzen[resizeKey] = null;
6149          },
6150          mouseMoveHandler: function (e) {
6151              if (Radzen[resizeKey]) {
6152                  var widthFloat = (Radzen[resizeKey].width - (Radzen.isRTL(cell) ? -1 : 1) * (Radzen[resizeKey].clientX - e.clientX));
6153
6154                  if (widthFloat < minWidth) {
6155                      widthFloat = minWidth;
6156                  }
6157
6158                  if (!Number.isNaN(maxWidth) && widthFloat > maxWidth) {
6159                      widthFloat = maxWidth;
6160                  }
6161
6162                  if (widthFloat !== Radzen[resizeKey].width) {
6163                      Radzen[resizeKey].resized = true;
6164                  }
6165
6166                  var width = widthFloat + 'px';
6167
6168                  if (col) {
6169                      col.style.width = width;
6170                  } else {
6171                      cell.style.width = width;
6172                  }
6173
6174                  if (hasFrozenColumns) {
6175                      Radzen.updateFrozenColumnPositions(gridElement);
6176                  }
6177              }
6178          },
6179          touchMoveHandler: function (e) {
6180              if (e.targetTouches[0]) {
6181                  e.preventDefault();
6182                  Radzen[resizeKey].mouseMoveHandler(e.targetTouches[0]);
6183              }
6184          }
6185      };
6186      el.style.width = "100%";
6187      document.addEventListener('mousemove', Radzen[resizeKey].mouseMoveHandler);
6188      document.addEventListener('mouseup', Radzen[resizeKey].mouseUpHandler);
6189      document.addEventListener('touchmove', Radzen[resizeKey].touchMoveHandler, { passive: false })
6190      document.addEventListener('touchend', Radzen[resizeKey].mouseUpHandler);
6191  },
6192      startSplitterResize: function(id,
6193        splitter,
6194        paneId,
6195        paneNextId,
6196        orientation,
6197        clientPos,
6198        minValue,
6199        maxValue,
6200        minNextValue,
6201        maxNextValue) {
6202
6203        var el = document.getElementById(id);
6204        var pane = document.getElementById(paneId);
6205        var paneNext = document.getElementById(paneNextId);
6206        var separator = pane ? document.getElementById(pane.id + '-resize') : null;
6207        var paneLength;
6208        var paneNextLength;
6209        var panePerc;
6210        var paneNextPerc;
6211        var isHOrientation=orientation == 'Horizontal';
6212
6213        var totalLength = 0.0;
6214        Array.from(el.children).forEach(element => {
6215            totalLength += isHOrientation
6216                ? element.getBoundingClientRect().width
6217                : element.getBoundingClientRect().height;
6218        });
6219
6220        if (pane) {
6221            paneLength = isHOrientation
6222                ? pane.getBoundingClientRect().width
6223                : pane.getBoundingClientRect().height;
6224
6225            panePerc = (paneLength / totalLength * 100) + '%';
6226        }
6227
6228        if (paneNext) {
6229            paneNextLength = isHOrientation
6230                ? paneNext.getBoundingClientRect().width
6231                : paneNext.getBoundingClientRect().height;
6232
6233            paneNextPerc = (paneNextLength / totalLength * 100) + '%';
6234        }
6235
6236        function ensurevalue(value) {
6237            if (!value)
6238                return null;
6239
6240            value=value.trim().toLowerCase();
6241
6242            if (value.endsWith("%"))
6243                return totalLength*parseFloat(value)/100;
6244
6245            if (value.endsWith("px"))
6246                return parseFloat(value);
6247
6248            throw 'Invalid value';
6249        }
6250
6251        minValue=ensurevalue(minValue);
6252        maxValue=ensurevalue(maxValue);
6253        minNextValue=ensurevalue(minNextValue);
6254        maxNextValue=ensurevalue(maxNextValue);
6255
6256        Radzen[el] = {
6257            clientPos: clientPos,
6258            panePerc: parseFloat(panePerc),
6259            paneNextPerc: isFinite(parseFloat(paneNextPerc)) ? parseFloat(paneNextPerc) : 0,
6260            paneLength: paneLength,
6261            paneNextLength: isFinite(paneNextLength) ? paneNextLength : 0,
6262            mouseUpHandler: function(e) {
6263                if (Radzen[el]) {
6264                    try { splitter.invokeMethodAsync(
6265                        'RadzenSplitter.OnPaneResized',
6266                        parseInt(pane.getAttribute('data-index')),
6267                        parseFloat(pane.style.flexBasis),
6268                        paneNext ? parseInt(paneNext.getAttribute('data-index')) : null,
6269                        paneNext ? parseFloat(paneNext.style.flexBasis) : null
6270                    ); } catch { }
6271
6272                    document.removeEventListener('pointerup', Radzen[el].mouseUpHandler);
6273                    document.removeEventListener('pointercancel', Radzen[el].mouseUpHandler);
6274                    document.removeEventListener('pointermove', Radzen[el].mouseMoveHandler);
6275                    el.removeEventListener('touchmove', preventDefaultAndStopPropagation);
6276                    Radzen[el] = null;
6277                }
6278            },
6279            mouseMoveHandler: function(e) {
6280                if (Radzen[el]) {
6281
6282                    if (e.buttons === 0) {
6283                        Radzen[el].mouseUpHandler(e);
6284                        return;
6285                    }
6286
6287                    try { splitter.invokeMethodAsync(
6288                        'RadzenSplitter.OnPaneResizing'
6289                    ); } catch { }
6290
6291                    var spacePerc = Radzen[el].panePerc + Radzen[el].paneNextPerc;
6292                    var spaceLength = Radzen[el].paneLength + Radzen[el].paneNextLength;
6293
6294                    var length = (Radzen[el].paneLength -
6295                        (isHOrientation && Radzen.isRTL(e.target) ? -1 : 1) * (Radzen[el].clientPos - (isHOrientation ? e.clientX : e.clientY)));
6296
6297                    if (length > spaceLength)
6298                        length = spaceLength;
6299
6300                    if (minValue && length < minValue) length = minValue;
6301                    if (maxValue && length > maxValue) length = maxValue;
6302
6303                    if (paneNext) {
6304                        var nextSpace=spaceLength-length;
6305                        if (minNextValue && nextSpace < minNextValue) length = spaceLength-minNextValue;
6306                        if (maxNextValue && nextSpace > maxNextValue) length = spaceLength-maxNextValue;
6307                    }
6308
6309                    var perc = length / Radzen[el].paneLength;
6310                    if (!isFinite(perc)) {
6311                        perc = 1;
6312                        Radzen[el].panePerc = 0.1;
6313                        Radzen[el].paneLength =isHOrientation
6314                            ? pane.getBoundingClientRect().width
6315                            : pane.getBoundingClientRect().height;
6316                    }
6317
6318                    var newPerc =  Radzen[el].panePerc * perc;
6319                    if (newPerc < 0) newPerc = 0;
6320                    if (newPerc > 100) newPerc = 100;
6321
6322                    pane.style.flexBasis = newPerc + '%';
6323                    if (paneNext)
6324                        paneNext.style.flexBasis = (spacePerc - newPerc) + '%';
6325
6326                    if (separator) {
6327                        var valueMin = parseFloat(separator.getAttribute('aria-valuemin')) || 0;
6328                        var valueMax = parseFloat(separator.getAttribute('aria-valuemax'));
6329                        if (!isFinite(valueMax)) valueMax = 100;
6330                        var valueNow = Math.round(newPerc);
6331                        if (valueNow < valueMin) valueNow = valueMin;
6332                        if (valueNow > valueMax) valueNow = valueMax;
6333                        separator.setAttribute('aria-valuenow', valueNow);
6334                    }
6335                }
6336            },
6337            touchMoveHandler: function(e) {
6338                if (e.targetTouches[0]) {
6339                    Radzen[el].mouseMoveHandler(e.targetTouches[0]);
6340                }
6341            }
6342          };
6343
6344        const preventDefaultAndStopPropagation = (ev) => {
6345            ev.preventDefault();
6346            ev.stopPropagation();
6347        };
6348          document.addEventListener('pointerup', Radzen[el].mouseUpHandler);
6349          document.addEventListener('pointercancel', Radzen[el].mouseUpHandler);
6350          document.addEventListener('pointermove', Radzen[el].mouseMoveHandler);
6351          el.addEventListener('touchmove', preventDefaultAndStopPropagation, { passive: false });
6352    },
6353    resizeSplitter(id, e) {
6354        var el = document.getElementById(id);
6355        if (el && Radzen[el]) {
6356            Radzen[el].mouseMoveHandler(e);
6357            if (Radzen[el]) {
6358                Radzen[el].mouseUpHandler(e);
6359            }
6360        }
6361    },
6362    openWaiting: function() {
6363        if (document.documentElement.scrollHeight > document.documentElement.clientHeight) {
6364            document.body.classList.add('no-scroll');
6365        }
6366        if (Radzen.WaitingIntervalId != null) {
6367            clearInterval(Radzen.WaitingIntervalId);
6368        }
6369
6370        setTimeout(function() {
6371                var timerObj = document.getElementsByClassName('rz-waiting-timer');
6372                if (timerObj.length == 0) return;
6373                var timerStart = new Date().getTime();
6374                Radzen.WaitingIntervalId = setInterval(function() {
6375                        if (timerObj == null || timerObj[0] == null) {
6376                            clearInterval(Radzen.WaitingIntervalId);
6377                        } else {
6378                            var time = new Date(new Date().getTime() - timerStart);
6379                            timerObj[0].innerHTML = Math.floor(time / 1000) + "." + Math.floor((time % 1000) / 100);
6380                        }
6381                    },
6382                    100);
6383            },
6384            100);
6385    },
6386    closeWaiting: function() {
6387        document.body.classList.remove('no-scroll');
6388        if (Radzen.WaitingIntervalId != null) {
6389            clearInterval(Radzen.WaitingIntervalId);
6390            Radzen.WaitingIntervalId = null;
6391        }
6392    },
6393    toggleDictation: function (componentRef, language) {
6394        var restarts = 0;
6395
6396        function start() {
6397            const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
6398
6399            if (!SpeechRecognition) {
6400                return;
6401            }
6402
6403            radzenRecognition = new SpeechRecognition();
6404            radzenRecognition.componentRef = componentRef;
6405            radzenRecognition.continuous = true;
6406
6407            if (language) {
6408                radzenRecognition.lang = language;
6409            }
6410
6411            radzenRecognition.onresult = function (event) {
6412                if (event.results.length < 1) {
6413                    return;
6414                }
6415
6416                restarts = 0;
6417
6418                let current = event.results[event.results.length - 1][0]
6419                let result = current.transcript;
6420
6421                try { suppressDisposed(componentRef.invokeMethodAsync("OnResult", result)); } catch { }
6422            };
6423            radzenRecognition.onerror = function (event) {
6424                if (event.error == 'not-allowed' || event.error == 'service-not-allowed' || event.error == 'audio-capture' || event.error == 'language-not-supported') {
6425                    this.stopRequested = true;
6426                }
6427            };
6428            radzenRecognition.onend = function (event) {
6429                if (!this.stopRequested && restarts++ < 10) {
6430                    start();
6431                    return;
6432                }
6433
6434                if (this == radzenRecognition) {
6435                    radzenRecognition = null;
6436                }
6437
6438                try { suppressDisposed(componentRef.invokeMethodAsync("StopRecording")); } catch { }
6439            };
6440            radzenRecognition.start();
6441        }
6442
6443        if (radzenRecognition) {
6444            radzenRecognition.stopRequested = true;
6445            if (radzenRecognition.componentRef._id != componentRef._id) {
6446                radzenRecognition.addEventListener('end', function () { start(); });
6447            }
6448            radzenRecognition.stop();
6449        } else {
6450            start();
6451        }
6452    },
6453    openChartTooltip: function (chart, x, y, id, instance, callback) {
6454        var existing = document.getElementById(id);
6455        var wasOpen = existing && existing.style.display != 'none' && existing.offsetParent != null;
6456
6457        Radzen.closeTooltip(id);
6458
6459        var chartRect = chart.getBoundingClientRect();
6460        x = Math.max(2, chartRect.left + x);
6461        y = Math.max(2, chartRect.top + y);
6462        Radzen.openPopup(chart, id, false, null, x, y, instance, callback, true, false, false);
6463
6464        var popup = document.getElementById(id);
6465        if (!popup) {
6466            return;
6467        }
6468        var tooltipContent = popup.children[0];
6469
6470        var inner = tooltipContent.querySelector('.rz-chart-tooltip-content');
6471        if (inner) {
6472            inner.classList.remove('rz-chart-tooltip-enter');
6473            if (!wasOpen) {
6474                void inner.offsetWidth;
6475                inner.classList.add('rz-chart-tooltip-enter');
6476            }
6477        }
6478        var tooltipContentRect = tooltipContent.getBoundingClientRect();
6479        var tooltipContentClassName = 'rz-top-chart-tooltip';
6480        if (y - tooltipContentRect.height < 0) {
6481            tooltipContentClassName = 'rz-bottom-chart-tooltip';
6482        }
6483        tooltipContent.classList.remove('rz-top-chart-tooltip');
6484        tooltipContent.classList.remove('rz-bottom-chart-tooltip');
6485        tooltipContent.classList.add(tooltipContentClassName);
6486    },
6487    createScrollListener: function (ref, selectors, selector) {
6488        let currentSelector;
6489        const container = selector ? document.querySelector(selector) : document.documentElement;
6490        const elements = selectors.map(document.querySelector, document);
6491
6492        let rafId = null;
6493
6494        const scrollHandler = () => {
6495            if (rafId !== null) {
6496                return;
6497            }
6498
6499            rafId = requestAnimationFrame(() => {
6500                rafId = null;
6501
6502                const isHtml = !selector || container.tagName === 'HTML';
6503                const threshold = isHtml ? 10 : container.getBoundingClientRect().top + 10;
6504                const atBottom = isHtml
6505                    ? window.scrollY > 0 && (window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight - 2
6506                    : container.scrollTop > 0 && (container.scrollTop + container.clientHeight) >= container.scrollHeight - 2;
6507
6508                let match;
6509
6510                if (atBottom) {
6511                    for (let i = selectors.length - 1; i >= 0; i--) {
6512                        if (elements[i]) {
6513                            match = selectors[i];
6514                            break;
6515                        }
6516                    }
6517                } else {
6518                    for (let i = elements.length - 1; i >= 0; i--) {
6519                        const element = elements[i];
6520
6521                        if (!element) {
6522                            continue;
6523                        }
6524
6525                        if (element.getBoundingClientRect().top <= threshold) {
6526                            match = selectors[i];
6527                            break;
6528                        }
6529                    }
6530                }
6531
6532                if (match !== currentSelector) {
6533                    currentSelector = match;
6534                    try {
6535                        ref.invokeMethodAsync('ScrollIntoView', currentSelector);
6536                    } catch {
6537                    }
6538                }
6539            });
6540        };
6541
6542        const scrollTarget = selector ? container : window;
6543        scrollTarget.addEventListener('scroll', scrollHandler, { passive: true });
6544        window.addEventListener('resize', scrollHandler, { passive: true });
6545
6546        scrollHandler();
6547
6548        return {
6549            dispose() {
6550                scrollTarget.removeEventListener('scroll', scrollHandler);
6551                window.removeEventListener('resize', scrollHandler);
6552            }
6553        };
6554    },
6555    setTheme: function (href, wcagHref) {
6556      const theme = document.getElementById('radzen-theme-link');
6557      if (theme && theme.sheet && theme.sheet.href === theme.href && theme.href != href) {
6558        theme.href = href;
6559      }
6560
6561      let wcagTheme = document.getElementById('radzen-wcag-theme-link');
6562
6563      if (!wcagTheme && wcagHref) {
6564        wcagTheme = document.createElement('link');
6565        wcagTheme.id = 'radzen-wcag-theme-link';
6566        wcagTheme.rel = 'stylesheet';
6567        wcagTheme.href = wcagHref;
6568        theme.parentNode.insertBefore(wcagTheme, theme.nextSibling);
6569      } else if (wcagTheme && wcagTheme.href != wcagHref) {
6570        if (!wcagHref) {
6571          wcagTheme.parentNode.removeChild(wcagTheme);
6572          return;
6573        } else {
6574          wcagTheme.href = wcagHref;
6575        }
6576      }
6577    },
6578    createDraggable: function(element, ref, onDragStart) {
6579      function handleDragStart(e) {
6580        e.dataTransfer.setData('', e.target.id);
6581        try { suppressDisposed(ref.invokeMethodAsync(onDragStart)); } catch { }
6582      }
6583      element.draggable = true;
6584      element.addEventListener('dragstart', handleDragStart);
6585      return {
6586        dispose() {
6587          element.removeEventListener('dragstart', handleDragStart);
6588        }
6589      };
6590    }
6591};
6592
6593Radzen.registerFabMenu = function(element, dotnet){
6594  if(!element) return;
6595  if(element.__rzOutsideClickHandler){
6596    document.removeEventListener('click', element.__rzOutsideClickHandler);
6597    delete element.__rzOutsideClickHandler;
6598  }
6599  const handler = function(e){
6600    if(!element.contains(e.target)){
6601      try { suppressDisposed(dotnet.invokeMethodAsync('CloseAsync')); } catch { }
6602    }
6603  };
6604  element.__rzOutsideClickHandler = handler;
6605  document.addEventListener('click', handler);
6606};
6607Radzen.unregisterFabMenu = function(element){
6608  if(!element) return;
6609  const handler = element.__rzOutsideClickHandler;
6610  if(handler){
6611    document.removeEventListener('click', handler);
6612    delete element.__rzOutsideClickHandler;
6613  }
6614};
6615Radzen.chatIsNearBottom = function(container, threshold) {
6616  if (!container) return true;
6617  return container.scrollHeight - container.scrollTop - container.clientHeight <= (threshold || 50);
6618};
6619Radzen.chatScrollToBottom = function(container) {
6620  if (!container) return;
6621  container.scrollTop = container.scrollHeight;
6622};
6623Radzen.noop = function() {};
6624Radzen.getCookies = function() {
6625  return document.cookie;
6626};
6627Radzen.setCookie = function(cookie) {
6628  document.cookie = cookie;
6629};
6630Radzen.scrollToTop = function(elementId) {
6631  try { var el = document.getElementById(elementId); if (el) el.scrollTop = 0; } catch(e) {}
6632};
6633Radzen.scrollIntoView = function(selector) {
6634  var el = document.querySelector(selector);
6635  if (el) el.scrollIntoView();
6636};
6637Radzen.delayedFocus = function(elementId, delay) {
6638  setTimeout(function() { var el = document.getElementById(elementId); if (el) el.focus(); }, delay || 200);
6639};
6640Radzen.invokeRadzenCallback = function(key) {
6641  var fn = Radzen[key];
6642  if (typeof fn === 'function') fn();
6643};
6644Radzen.chatScrollAfterRender = function(selector, delay) {
6645  setTimeout(function() {
6646    var container = document.querySelector(selector);
6647    if (container) container.scrollTop = container.scrollHeight;
6648  }, delay || 100);
6649};
6650Radzen.createAutoComplete = function(el, popupId, openOnFocus, instance, openCallback, closeCallback) {
6651  if (!el) return { dispose: function() {} };
6652  var input = el.querySelector('input.rz-autocomplete-input, textarea.rz-autocomplete-input');
6653  var list = el.querySelector('.rz-autocomplete-list');
6654  if (!input) return { dispose: function() {} };
6655  function open() {
6656    var popup = document.getElementById(popupId);
6657    var wasOpen = popup && popup.style.display == 'block';
6658    Radzen.openPopup(input.parentNode, popupId, true, null, null, null, instance, closeCallback);
6659    if (!wasOpen && instance && openCallback) {
6660      try { suppressDisposed(instance.invokeMethodAsync(openCallback)); } catch { }
6661    }
6662  }
6663  function onInput() { open(); }
6664  function onFocus() { open(); }
6665  function clearActive() { Radzen.activeElement = null; }
6666  input.addEventListener('input', onInput);
6667  input.addEventListener('blur', clearActive);
6668  if (openOnFocus) input.addEventListener('focus', onFocus);
6669  if (list) list.addEventListener('mousedown', clearActive);
6670  return { dispose: function() {
6671    input.removeEventListener('input', onInput);
6672    input.removeEventListener('blur', clearActive);
6673    if (openOnFocus) input.removeEventListener('focus', onFocus);
6674    if (list) list.removeEventListener('mousedown', clearActive);
6675  }};
6676};
6677Radzen.createDataGrid = function(el) {
6678  if (!el) return { dispose: function() {} };
6679  function onClick(e) {
6680    var label = e.target.closest('.rz-cell-filter-label');
6681    if (label) { e.preventDefault(); }
6682    var btn = e.target.closest('button[aria-haspopup][aria-controls]');
6683    if (btn && !btn.closest('.rz-datepicker') && el.contains(btn) && label && label.contains(btn) && btn.getAttribute('aria-controls')) {
6684      Radzen.togglePopup(btn.parentNode, btn.getAttribute('aria-controls'));
6685    }
6686  }
6687  function onMousedown(e) {
6688    var btn = e.target.closest('.rz-apply-filter');
6689    if (btn) { Radzen.blur(btn, e); }
6690  }
6691  el.addEventListener('click', onClick);
6692  el.addEventListener('mousedown', onMousedown);
6693  return { dispose: function() {
6694    el.removeEventListener('click', onClick);
6695    el.removeEventListener('mousedown', onMousedown);
6696  }};
6697};
6698Radzen.createDropDown = function(el) {
6699  if (!el) return { dispose: function() {} };
6700  function onMousedown() { Radzen.activeElement = null; }
6701  function onFilterClick(e) {
6702    var input = e.target.closest('.rz-multiselect-filter-container input');
6703    if (input) { e.preventDefault(); e.stopPropagation(); }
6704  }
6705  el.addEventListener('mousedown', onMousedown);
6706  el.addEventListener('click', onFilterClick, true);
6707  return { dispose: function() {
6708    el.removeEventListener('mousedown', onMousedown);
6709    el.removeEventListener('click', onFilterClick, true);
6710  }};
6711};
6712Radzen.registerProfileMenuClickAway = function (el, ref) {
6713  if (!el) return;
6714  Radzen.unregisterProfileMenuClickAway(el);
6715  el.profileMenuClickAway = function (e) {
6716    if (!document.contains(el)) {
6717      Radzen.unregisterProfileMenuClickAway(el);
6718      return;
6719    }
6720    if (el.contains(e.target)) {
6721      var item = e.target.closest('.rz-navigation-item');
6722      if (item && item.querySelector('.rz-navigation-menu')) {
6723        return;
6724      }
6725    }
6726    try { suppressDisposed(ref.invokeMethodAsync('CloseOnClickAway')); } catch { }
6727  };
6728  document.addEventListener('click', el.profileMenuClickAway);
6729};
6730Radzen.unregisterProfileMenuClickAway = function (el) {
6731  if (el && el.profileMenuClickAway) {
6732    document.removeEventListener('click', el.profileMenuClickAway);
6733    delete el.profileMenuClickAway;
6734  }
6735};
6736Radzen.createFileInput = function(el) {
6737  if (!el) return { dispose: function() {} };
6738  var choose = el.querySelector('.rz-fileupload-choose');
6739  var fileInput = el.querySelector('input[type=file]');
6740  function onKeydown(e) {
6741    if (e.keyCode === 32 || e.keyCode === 13) {
6742      e.preventDefault();
6743      var child = choose.firstElementChild;
6744      if (child) child.click();
6745    }
6746  }
6747  function onStopKeydown(e) { e.stopPropagation(); }
6748  function onChange(e) { Radzen.uploadInputChange(e, null, false, false, true); }
6749  if (choose) choose.addEventListener('keydown', onKeydown);
6750  if (fileInput) {
6751    fileInput.addEventListener('keydown', onStopKeydown);
6752    fileInput.addEventListener('change', onChange);
6753  }
6754  return { dispose: function() {
6755    if (choose) choose.removeEventListener('keydown', onKeydown);
6756    if (fileInput) {
6757      fileInput.removeEventListener('keydown', onStopKeydown);
6758      fileInput.removeEventListener('change', onChange);
6759    }
6760  }};
6761};
6762Radzen.createMask = function(el, id, mask, pattern, charPattern) {
6763  if (!el) return { dispose: function() {} };
6764  function onInput() { Radzen.mask(id, mask, pattern, charPattern); }
6765  el.addEventListener('input', onInput);
6766  return { dispose: function() { el.removeEventListener('input', onInput); }};
6767};
6768Radzen.createNumeric = function(el, isInteger, separator, min, max, isNullable) {
6769  if (!el) return { dispose: function() {} };
6770  var input = el.querySelector('.rz-numeric-input');
6771  if (!input) return { dispose: function() {} };
6772  function onKeypress(e) { Radzen.numericKeyPress(e, isInteger, separator); }
6773  function onBlur(e) { Radzen.numericOnInput(e, min, max, isNullable); }
6774  function onPaste(e) { Radzen.numericOnPaste(e, min, max); }
6775  input.addEventListener('keypress', onKeypress);
6776  if (min !== null || max !== null) {
6777    input.addEventListener('blur', onBlur);
6778    input.addEventListener('paste', onPaste);
6779  }
6780  return { dispose: function() {
6781    input.removeEventListener('keypress', onKeypress);
6782    input.removeEventListener('blur', onBlur);
6783    input.removeEventListener('paste', onPaste);
6784  }};
6785};
6786Radzen.menuClick = function (e) {
6787  var item = e.target.closest('.rz-navigation-item-wrapper');
6788  if (!item) return;
6789  var menu = item.closest('[data-click-to-open]');
6790  if (!menu) return;
6791  var clickToOpen = menu.getAttribute('data-click-to-open') === 'true';
6792  var navItem = item.closest('.rz-navigation-item');
6793  var hasChildren = navItem && navItem.querySelector('.rz-navigation-menu');
6794  if (clickToOpen || hasChildren) {
6795    Radzen.toggleMenuItem(item);
6796  } else {
6797    Radzen.toggleMenuItem(item, e, false);
6798  }
6799};
6800document.addEventListener('click', Radzen.menuClick);
6801Radzen.createMenu = function(el, clickToOpen) {
6802  if (!el) return { dispose: function() {} };
6803  var hoverItems = [];
6804  if (!clickToOpen) {
6805    el.querySelectorAll('.rz-navigation-item').forEach(function(navItem) {
6806      if (!navItem.querySelector('.rz-navigation-menu')) return;
6807      var li = navItem.closest('li') || navItem;
6808      function onEnter(e) { Radzen.toggleMenuItem(li, e, true, false); }
6809      function onLeave(e) { Radzen.toggleMenuItem(li, e, false, false); }
6810      li.addEventListener('mouseenter', onEnter);
6811      li.addEventListener('mouseleave', onLeave);
6812      hoverItems.push({ el: li, onEnter: onEnter, onLeave: onLeave });
6813    });
6814  }
6815  return { dispose: function() {
6816    hoverItems.forEach(function(h) {
6817      h.el.removeEventListener('mouseenter', h.onEnter);
6818      h.el.removeEventListener('mouseleave', h.onLeave);
6819    });
6820  }};
6821};
6822Radzen.createUpload = function(el, url, auto, multiple, parameterName, method, stream) {
6823  if (!el) return { dispose: function() {} };
6824  var choose = el.querySelector('.rz-fileupload-choose');
6825  var fileInput = el.querySelector('input[type=file]');
6826  function onKeydown(e) {
6827    if (e.keyCode === 32 || e.keyCode === 13) {
6828      e.preventDefault();
6829      var child = choose.firstElementChild;
6830      if (child) child.click();
6831    }
6832  }
6833  function onStopKeydown(e) { e.stopPropagation(); }
6834  function onChange(e) {
6835    Radzen.uploadInputChange(e, url, auto, multiple, true, parameterName, method, stream);
6836  }
6837  function onImgError() { this.style.display = 'none'; }
6838  if (choose) choose.addEventListener('keydown', onKeydown);
6839  if (fileInput) {
6840    fileInput.addEventListener('keydown', onStopKeydown);
6841    if (url) fileInput.addEventListener('change', onChange);
6842  }
6843  el.querySelectorAll('.rz-fileupload-row img').forEach(function(img) {
6844    img.addEventListener('error', onImgError);
6845  });
6846  return { dispose: function() {
6847    if (choose) choose.removeEventListener('keydown', onKeydown);
6848    if (fileInput) {
6849      fileInput.removeEventListener('keydown', onStopKeydown);
6850      if (url) fileInput.removeEventListener('change', onChange);
6851    }
6852    el.querySelectorAll('.rz-fileupload-row img').forEach(function(img) {
6853      img.removeEventListener('error', onImgError);
6854    });
6855  }};
6856};
6857class Spreadsheet {
6858  constructor(element, dotNetRef, shortcuts) {
6859    this.element = element;
6860    this.dotNetRef = dotNetRef;
6861    this.shortcuts = shortcuts || {}; // map of key -> isGlobal (true = global, false = grid-only)
6862    this.rtl = Radzen.isRTL(element);
6863    this.pendingKeys = null;
6864    this.element.addEventListener('keydown', this.onKeyDown);
6865    this.element.addEventListener('focusin', this.onFocusIn);
6866    this.element.addEventListener('pointerdown', this.onPointerDown);
6867    this.element.addEventListener('dblclick', this.onDoubleClick);
6868    this.element.addEventListener('contextmenu', this.onContextMenu);
6869    addEventListener('paste', this.onPaste);
6870    addEventListener('copy', this.onCopy);
6871  }
6872
6873  onPaste = (e) => {
6874    if (this.isActive(e.target)) {
6875      var text = e.clipboardData.getData('text/plain');
6876      this.dotNetRef.invokeMethodAsync('OnPasteAsync', text);
6877    }
6878  }
6879
6880  onCopy = async (e) => {
6881    if (this.isActive(e.target)) {
6882      this.dotNetRef.invokeMethodAsync('OnCopyAsync');
6883    }
6884  }
6885
6886  isActive = (target) => {
6887    return target == this.element || this.element.contains(target);
6888  }
6889
6890  copyToClipboard = async (text) => {
6891    try {
6892      await navigator.clipboard.writeText(text);
6893    } catch {
6894      // Clipboard API failed (e.g. user activation expired)
6895    }
6896  }
6897
6898  readClipboardText = async () => {
6899    try {
6900      return await navigator.clipboard.readText();
6901    } catch {
6902      return null;
6903    }
6904  }
6905
6906  onPointerDown = async (e) => {
6907    if (e.button != 0) return;
6908
6909    this.pendingKeys = null;
6910
6911    this.rtl = Radzen.isRTL(this.element);
6912
6913    if (e.target.matches('.rz-spreadsheet-autofill-handle')) {
6914      addEventListener('pointermove', this.onAutofillPointerMove);
6915      addEventListener('pointerup', this.onAutofillPointerUp);
6916      this.dotNetRef.invokeMethodAsync('OnAutofillPointerDownAsync', this.toEventArgs(e));
6917      e.preventDefault();
6918      e.stopPropagation();
6919      return;
6920    }
6921
6922    if (e.target.matches('.rz-spreadsheet-cell')) {
6923      const cell = e.target;
6924      const row = +cell.dataset.row;
6925      const column = +cell.dataset.column;
6926      addEventListener('pointerup', this.onCellPointerUp);
6927      addEventListener('pointermove', this.onCellPointerMove);
6928      if (!(await this.dotNetRef.invokeMethodAsync('OnCellPointerDownAsync', { row, column, pointer: this.toEventArgs(e) }))) {
6929        removeEventListener('pointermove', this.onCellPointerMove);
6930      }
6931    } else if (e.target.matches('.rz-spreadsheet-row-header')) {
6932      const row = +e.target.dataset.row;
6933      addEventListener('pointerup', this.onRowPointerUp);
6934      addEventListener('pointermove', this.onRowPointerMove);
6935      if (!(await this.dotNetRef.invokeMethodAsync('OnRowPointerDownAsync', { row, pointer: this.toEventArgs(e) }))) {
6936        removeEventListener('pointermove', this.onRowPointerMove);
6937      }
6938    } else if (e.target.matches('.rz-spreadsheet-column-header')) {
6939      const column = +e.target.dataset.column;
6940      addEventListener('pointerup', this.onColumnPointerUp);
6941      addEventListener('pointermove', this.onColumnPointerMove);
6942      if (!(await this.dotNetRef.invokeMethodAsync('OnColumnPointerDownAsync', { column, pointer: this.toEventArgs(e) }))) {
6943        removeEventListener('pointermove', this.onColumnPointerMove);
6944      }
6945    } else if (e.target.matches('.rz-spreadsheet-resize-handle')) {
6946      const direction = e.target.dataset.direction;
6947      addEventListener('pointermove', this.onDrawingResizeMove);
6948      addEventListener('pointerup', this.onDrawingResizeUp);
6949      if (!(await this.dotNetRef.invokeMethodAsync('OnDrawingResizePointerDownAsync', { direction, pointer: this.toEventArgs(e) }))) {
6950        removeEventListener('pointermove', this.onDrawingResizeMove);
6951        removeEventListener('pointerup', this.onDrawingResizeUp);
6952      }
6953      e.preventDefault();
6954      e.stopPropagation();
6955    } else if (e.target.closest('.rz-spreadsheet-image') || e.target.closest('.rz-spreadsheet-chart')) {
6956      addEventListener('pointermove', this.onDrawingMoveMove);
6957      addEventListener('pointerup', this.onDrawingMoveUp);
6958      this.dotNetRef.invokeMethodAsync('OnDrawingMovePointerDownAsync', this.toEventArgs(e));
6959      e.preventDefault();
6960    } else if (e.target.matches('.rz-spreadsheet-column-resize-handle')) {
6961      const column = +e.target.dataset.column;
6962        addEventListener('pointermove', this.onColumnResizeMove);
6963        addEventListener('pointerup', this.onColumnResizeUp);
6964      if (!(await this.dotNetRef.invokeMethodAsync('OnColumnResizePointerDownAsync', { column, pointer: this.toEventArgs(e) }))) {
6965        removeEventListener('pointermove', this.onColumnResizeMove);
6966        removeEventListener('pointerup', this.onColumnResizeUp);
6967      }
6968      e.preventDefault();
6969      e.stopPropagation();
6970    } else if (e.target.matches('.rz-spreadsheet-row-resize-handle')) {
6971      const row = +e.target.dataset.row;
6972      addEventListener('pointermove', this.onRowResizeMove);
6973      addEventListener('pointerup', this.onRowResizeUp);
6974      if (!(await this.dotNetRef.invokeMethodAsync('OnRowResizePointerDownAsync', { row, pointer: this.toEventArgs(e) }))) {
6975        removeEventListener('pointermove', this.onRowResizeMove);
6976        removeEventListener('pointerup', this.onRowResizeUp);
6977      }
6978      e.preventDefault();
6979      e.stopPropagation();
6980    }
6981  }
6982
6983  onContextMenu = (e) => {
6984    if (e.target.matches('.rz-spreadsheet-cell')) {
6985      e.preventDefault();
6986      const row = +e.target.dataset.row;
6987      const column = +e.target.dataset.column;
6988      this.dotNetRef.invokeMethodAsync('OnCellContextMenuAsync', { row, column, pointer: this.toEventArgs(e) });
6989    } else if (e.target.matches('.rz-spreadsheet-row-header')) {
6990      e.preventDefault();
6991      const row = +e.target.dataset.row;
6992      this.dotNetRef.invokeMethodAsync('OnRowContextMenuAsync', { row, pointer: this.toEventArgs(e) });
6993    } else if (e.target.matches('.rz-spreadsheet-column-header')) {
6994      e.preventDefault();
6995      const column = +e.target.dataset.column;
6996      this.dotNetRef.invokeMethodAsync('OnColumnContextMenuAsync', { column, pointer: this.toEventArgs(e) });
6997    } else if (e.target === this.element) {
6998      // Keyboard-triggered (Shift+F10 / Menu key) lands on the grid root, not a cell. Suppress the
6999      // browser's native menu; the C# Shift+F10 shortcut opens our menu at the active cell.
7000      e.preventDefault();
7001    }
7002  }
7003
7004  onDoubleClick = (e) => {
7005    if (e.target.matches('.rz-spreadsheet-cell')) {
7006      const cell = e.target;
7007      const row = +cell.dataset.row;
7008      const column = +cell.dataset.column;
7009      this.dotNetRef.invokeMethodAsync('OnCellDoubleClickAsync', { row, column, pointer: this.toEventArgs(e) });
7010    } else if (e.target.matches('.rz-spreadsheet-column-resize-handle')) {
7011      e.preventDefault();
7012      e.stopPropagation();
7013      const column = +e.target.dataset.column;
7014      this.dotNetRef.invokeMethodAsync('OnColumnResizeDoubleClickAsync', { row: 0, column, pointer: this.toEventArgs(e) });
7015    }
7016  }
7017
7018  measureTexts = (items) => {
7019    const probe = document.createElement('div');
7020    probe.className = 'rz-spreadsheet-cell';
7021    probe.style.visibility = 'hidden';
7022    this.element.appendChild(probe);
7023    const style = getComputedStyle(probe);
7024    const baseFont = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
7025    const padding = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0)
7026      + (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0);
7027    const baseFontSize = style.fontSize;
7028    const baseFontFamily = style.fontFamily;
7029    this.element.removeChild(probe);
7030
7031    this.measureCanvas ??= document.createElement('canvas');
7032    const context = this.measureCanvas.getContext('2d');
7033    context.direction = this.rtl ? 'rtl' : 'ltr';
7034
7035    return items.map(item => {
7036      // Invalid font shorthand assignments are silently ignored; reset so they fall back to the base font.
7037      context.font = baseFont;
7038      if (item.bold || item.italic || item.fontSize != null || item.fontFamily) {
7039        const size = item.fontSize != null ? item.fontSize + 'pt' : baseFontSize;
7040        const family = item.fontFamily ? `"${item.fontFamily.replaceAll('"', '\\"')}", ${baseFontFamily}` : baseFontFamily;
7041        context.font = `${item.italic ? 'italic ' : ''}${item.bold ? 'bold ' : ''}${size} ${family}`;
7042      }
7043      const width = context.measureText(item.text).width;
7044      // 3% over-fit absorbs metric differences between canvas and other renderers (e.g. Excel after export).
7045      return Number.isFinite(width) ? Math.ceil(width * 1.03 + padding) : 0;
7046    });
7047  }
7048
7049  onColumnPointerUp = (e) => {
7050    removeEventListener('pointermove', this.onColumnPointerMove);
7051    removeEventListener('pointerup', this.onColumnPointerUp);
7052    this.dotNetRef.invokeMethodAsync('OnSelectionPointerUpAsync');
7053  }
7054
7055  onColumnPointerMove = (e) => {
7056    this.invokeAsync('OnColumnPointerMoveAsync', e);
7057  }
7058
7059  onRowPointerMove = (e) => {
7060    this.invokeAsync('OnRowPointerMoveAsync', e);
7061  }
7062
7063  onRowPointerUp = (e) => {
7064    removeEventListener('pointermove', this.onRowPointerMove);
7065    removeEventListener('pointerup', this.onRowPointerUp);
7066    this.dotNetRef.invokeMethodAsync('OnSelectionPointerUpAsync');
7067  }
7068
7069  onCellPointerMove = (e) => {
7070    this.invokeAsync('OnCellPointerMoveAsync', e);
7071  }
7072
7073  onCellPointerUp = (e) => {
7074    removeEventListener('pointermove', this.onCellPointerMove);
7075    removeEventListener('pointerup', this.onCellPointerUp);
7076    this.dotNetRef.invokeMethodAsync('OnSelectionPointerUpAsync');
7077  }
7078
7079  onKeyDown = (e) => {
7080    let key = '';
7081
7082    if (e.ctrlKey || e.metaKey) {
7083      key += 'Ctrl+';
7084    }
7085
7086    if (e.altKey) {
7087      key += 'Alt+';
7088    }
7089
7090    if (e.shiftKey) {
7091      key += 'Shift+';
7092    }
7093
7094    key += e.code.replace('Key', '').replace('Digit', '').replace('Numpad', '');
7095
7096    // Grid context = focus on THIS spreadsheet's root or inside one of ITS cell/formula editors.
7097    // Scope the editor check to this.element so a sibling spreadsheet on the same page can never
7098    // match. Outside the grid (a toolbar button, sheet tab, ...) only global shortcuts act, so Tab
7099    // etc. stay native and the chrome is keyboard-navigable.
7100    let isGridContext = e.target === this.element;
7101    if (!isGridContext && e.target.closest) {
7102      const editorInput = e.target.closest('.rz-spreadsheet-editor-input');
7103      isGridContext = editorInput != null && this.element.contains(editorInput);
7104    }
7105
7106    const global = this.shortcuts[key]; // true = global, false = grid-only, undefined = not a shortcut
7107    if (global === true || (global === false && isGridContext)) {
7108      e.preventDefault();
7109    }
7110
7111    const printable = global === undefined && !e.ctrlKey && !e.metaKey && !e.altKey &&
7112      e.key.length === 1 && e.target === this.element;
7113
7114    // Prevent default for printable characters when not already editing.
7115    // Without this, the character gets inserted twice: once by StartEdit and
7116    // once by the browser's default insertText when the editor receives focus.
7117    if (printable) {
7118      e.preventDefault();
7119    }
7120
7121    if (printable) {
7122      if (this.pendingKeys != null) {
7123        this.pendingKeys += e.key;
7124        return;
7125      }
7126
7127      this.pendingKeys = '';
7128    } else if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
7129      this.pendingKeys = null;
7130    }
7131
7132    this.dotNetRef.invokeMethodAsync('OnKeyDownAsync', this.toEventArgs(e), isGridContext);
7133  }
7134
7135  onFocusIn = (e) => {
7136    const keys = this.pendingKeys;
7137
7138    this.pendingKeys = null;
7139
7140    if (!keys || !e.target.matches('.rz-spreadsheet-editor-input')) {
7141      return;
7142    }
7143
7144    e.target.innerText += keys;
7145
7146    const range = document.createRange();
7147    range.selectNodeContents(e.target);
7148    range.collapse(false);
7149
7150    const selection = getSelection();
7151    selection.removeAllRanges();
7152    selection.addRange(range);
7153
7154    e.target.dispatchEvent(new Event('input'));
7155  }
7156
7157  // F6 / Shift+F6 cycle focus between the spreadsheet regions in their visible top-to-bottom order:
7158  // ribbon tabs -> active toolbar -> formula bar -> grid -> sheet tabs. This is the advertised escape
7159  // from the grid (WCAG 2.1.2).
7160  focusAdjacent = (forward) => {
7161    // this.element IS this spreadsheet's root (.rz-spreadsheet, role=application). querySelector
7162    // only matches descendants of root, so regions from another spreadsheet on the page are never
7163    // matched.
7164    const root = this.element;
7165    const regions = [
7166      root.querySelector('[role="tablist"], .rz-tabs-nav'),
7167      root.querySelector('.rz-toolbar'),
7168      root.querySelector('.rz-spreadsheet-formula-editor .rz-spreadsheet-editor-input'),
7169      root,
7170      root.querySelector('.rz-spreadsheet-sheet-tabs')
7171    ].filter(r => r);
7172
7173    if (regions.length === 0) {
7174      return;
7175    }
7176
7177    // Regions nest (the ribbon tabview contains the toolbar; the root contains everything), so pick
7178    // the INNERMOST region that contains the focused element - otherwise an outer region captures
7179    // focus that belongs to a nested one and F6 gets stuck cycling between two regions.
7180    const active = document.activeElement;
7181    let index = -1;
7182    let best = null;
7183    regions.forEach((r, i) => {
7184      if ((r === active || r.contains(active)) && (best === null || best.contains(r))) {
7185        best = r;
7186        index = i;
7187      }
7188    });
7189    if (index === -1) {
7190      index = regions.indexOf(root);
7191    }
7192
7193    const next = (index + (forward ? 1 : -1) + regions.length) % regions.length;
7194    this.focusRegion(regions[next]);
7195  }
7196
7197  focusRegion = (region) => {
7198    // Focus the region itself when it is the tab stop: the grid root and the ribbon tablist both have
7199    // tabindex=0 (the tablist owns arrow-key tab switching via aria-activedescendant, so focusing an
7200    // individual tab would break the arrows), and the formula bar is a tabindex=-1 contenteditable.
7201    // Otherwise focus the region's first focusable child (e.g. the first toolbar button).
7202    if (region.tabIndex >= 0 || region.matches('.rz-spreadsheet-editor-input')) {
7203      region.focus();
7204      return;
7205    }
7206
7207    // Skip hidden focusables: inline-rendered but closed popups (color pickers, numeric steppers)
7208    // match the selector yet calling focus() on a hidden element silently fails, which would strand
7209    // the user on the previous region. Pick the first one that is actually rendered.
7210    const focusables = region.querySelectorAll('button:not([disabled]), a[href], input:not([disabled]), [tabindex]:not([tabindex="-1"]), [contenteditable="true"]');
7211    const target = [...focusables].find(el => el.offsetParent !== null || el.getClientRects().length > 0);
7212    (target || region).focus();
7213  }
7214
7215  // Opens the cell context menu at the active cell (Shift+F10 / ContextMenu key). The cell element is
7216  // scoped to this spreadsheet; a synthetic pointer carries its on-screen position.
7217  openCellContextMenu = (row, column) => {
7218    const cell = this.element.querySelector('.rz-spreadsheet-cell[data-row="' + row + '"][data-column="' + column + '"]');
7219    const rect = (cell || this.element).getBoundingClientRect();
7220    const pointer = {
7221      clientX: Math.round(rect.left + 8),
7222      clientY: Math.round(cell ? rect.bottom : rect.top + 8),
7223      button: 0, buttons: 0, pointerType: 'mouse', isPrimary: true
7224    };
7225    this.dotNetRef.invokeMethodAsync('OnCellContextMenuAsync', { row, column, pointer });
7226  }
7227
7228  invokeAsync(name, e) {
7229    this.dotNetRef.invokeMethodAsync(name, this.toEventArgs(e));
7230  }
7231
7232  toEventArgs(e) {
7233    return {
7234      key: e.key,
7235      code: this.rtl && (e.code === 'ArrowLeft' || e.code === 'ArrowRight')
7236        ? (e.code === 'ArrowLeft' ? 'ArrowRight' : 'ArrowLeft')
7237        : e.code,
7238      location: e.location,
7239      repeat: e.repeat,
7240      ctrlKey: e.ctrlKey,
7241      shiftKey: e.shiftKey,
7242      altKey: e.altKey,
7243      metaKey: e.metaKey,
7244      type: e.type,
7245      button: e.button,
7246      buttons: e.buttons,
7247      clientX: this.rtl ? -e.clientX : e.clientX,
7248      clientY: e.clientY,
7249      offsetX: this.rtl ? (e.target?.offsetWidth ?? 0) - e.offsetX : e.offsetX,
7250      offsetY: e.offsetY,
7251      pageX: e.pageX,
7252      pageY: e.pageY,
7253      screenX: e.screenX,
7254      screenY: e.screenY
7255    };
7256  }
7257
7258  dispose() {
7259    this.element.removeEventListener('keydown', this.onKeyDown);
7260    this.element.removeEventListener('focusin', this.onFocusIn);
7261    this.element.removeEventListener('pointerdown', this.onPointerDown);
7262    this.element.removeEventListener('dblclick', this.onDoubleClick);
7263    this.element.removeEventListener('contextmenu', this.onContextMenu);
7264    removeEventListener('paste', this.onPaste);
7265    removeEventListener('copy', this.onCopy);
7266  }
7267
7268  onColumnResizeMove = (e) => {
7269    this.invokeAsync('OnColumnResizePointerMoveAsync', e);
7270  }
7271
7272  onColumnResizeUp = (e) => {
7273    this.invokeAsync('OnColumnResizePointerUpAsync', e);
7274    removeEventListener('pointermove', this.onColumnResizeMove);
7275    removeEventListener('pointerup', this.onColumnResizeUp);
7276  }
7277
7278  onRowResizeMove = (e) => {
7279    this.invokeAsync('OnRowResizePointerMoveAsync', e);
7280  }
7281
7282  onRowResizeUp = (e) => {
7283    this.invokeAsync('OnRowResizePointerUpAsync', e);
7284    removeEventListener('pointermove', this.onRowResizeMove);
7285    removeEventListener('pointerup', this.onRowResizeUp);
7286  }
7287
7288  onDrawingResizeMove = (e) => {
7289    this.invokeAsync('OnDrawingResizePointerMoveAsync', e);
7290  }
7291
7292  onDrawingResizeUp = (e) => {
7293    this.invokeAsync('OnDrawingResizePointerUpAsync', e);
7294    removeEventListener('pointermove', this.onDrawingResizeMove);
7295    removeEventListener('pointerup', this.onDrawingResizeUp);
7296  }
7297
7298  onDrawingMoveMove = (e) => {
7299    this.invokeAsync('OnDrawingMovePointerMoveAsync', e);
7300  }
7301
7302  onDrawingMoveUp = (e) => {
7303    this.invokeAsync('OnDrawingMovePointerUpAsync', e);
7304    removeEventListener('pointermove', this.onDrawingMoveMove);
7305    removeEventListener('pointerup', this.onDrawingMoveUp);
7306  }
7307
7308  onAutofillPointerMove = (e) => {
7309    this.invokeAsync('OnAutofillPointerMoveAsync', e);
7310  }
7311
7312  onAutofillPointerUp = (e) => {
7313    this.dotNetRef.invokeMethodAsync('OnAutofillPointerUpAsync', this.toEventArgs(e));
7314    removeEventListener('pointermove', this.onAutofillPointerMove);
7315    removeEventListener('pointerup', this.onAutofillPointerUp);
7316  }
7317}
7318
7319Radzen.beginRangePickKeyHandler = function (dotNetRef) {
7320  Radzen.endRangePickKeyHandler();
7321  Radzen.rangePickKeyHandler = function (e) {
7322    if (e.key === 'Escape') {
7323      e.preventDefault();
7324      e.stopPropagation();
7325      dotNetRef.invokeMethodAsync('OnEscapeKeyAsync');
7326    }
7327  };
7328  document.addEventListener('keydown', Radzen.rangePickKeyHandler, true);
7329};
7330
7331Radzen.endRangePickKeyHandler = function () {
7332  if (Radzen.rangePickKeyHandler) {
7333    document.removeEventListener('keydown', Radzen.rangePickKeyHandler, true);
7334    Radzen.rangePickKeyHandler = null;
7335  }
7336};
7337
7338class SheetEditor {
7339  constructor(element, value, autoFocus, dotNetRef) {
7340    this.element = element;
7341    this.element.innerText = value;
7342    this.dotNetRef = dotNetRef;
7343    this.element.addEventListener('input', this.onInput);
7344    this.element.addEventListener('keydown', this.onKeyDown);
7345    this.element.addEventListener('blur', this.onBlur);
7346    this.element.addEventListener('focus', this.onFocus);
7347    this.element.addEventListener('paste', this.onPaste);
7348    document.addEventListener('selectionchange', this.onSelectionChange);
7349    if (autoFocus) {
7350      this.focus();
7351    }
7352  }
7353
7354  onPaste = (e) => {
7355    e.preventDefault();
7356    const text = e.clipboardData.getData('text/plain');
7357    const selection = getSelection();
7358    if (!selection.rangeCount) return;
7359    selection.deleteFromDocument();
7360    selection.getRangeAt(0).insertNode(document.createTextNode(text));
7361    selection.collapseToEnd();
7362    this.onInput();
7363  };
7364
7365  onSelectionChange = (e) => {
7366    const selection = getSelection();
7367    if (!selection.focusNode) return;
7368    const inside = selection.focusNode.parentElement == this.element || selection.focusNode == this.element;
7369    let caretPosition = -1;
7370
7371    if (inside && selection.isCollapsed)
7372    {
7373      caretPosition = selection.focusOffset;
7374    }
7375
7376    this.dotNetRef.invokeMethodAsync('OnSelectionChangeAsync', caretPosition);
7377  };
7378
7379  onInput = () => {
7380    // Browsers keep a placeholder <br> in an emptied contenteditable which makes innerText report "\n" for empty content
7381    const text = this.element.innerText;
7382    this.dotNetRef.invokeMethodAsync('OnInputAsync', text == '\n' ? '' : text);
7383  };
7384
7385  setValue = (value, moveCaretTo) => {
7386    this.element.innerText = value;
7387    if (moveCaretTo != null) {
7388      const range = document.createRange();
7389      const el = this.element.childNodes[0];
7390      range.selectNodeContents(el)
7391      range.setStart(el, 0);
7392      range.setEnd(el, moveCaretTo);
7393      range.collapse(false);
7394      const selection = getSelection();
7395      selection.removeAllRanges();
7396      selection.addRange(range);
7397    }
7398  };
7399
7400  onKeyDown = (e) => {
7401    if (e.key == 'Enter' && e.altKey) {
7402      e.preventDefault();
7403      e.stopPropagation();
7404
7405      const range = document.createRange();
7406      range.selectNodeContents(this.element);
7407      range.collapse(false);
7408      range.deleteContents();
7409
7410
7411      let br = document.createElement("br");
7412      range.insertNode(br);
7413
7414      if (!br.previousSibling || br.previousSibling.nodeName != 'BR') {
7415        br = br.cloneNode();
7416        range.insertNode(br);
7417      }
7418
7419      range.setStartAfter(br);
7420      range.setEndAfter(br);
7421
7422      const selection = getSelection();
7423      selection.removeAllRanges();
7424      selection.addRange(range);
7425    } else {
7426       const popup = document.querySelector('.rz-spreadsheet-highlight-popup .rz-state-highlight') != null;
7427       if (popup && (e.key == 'Tab' || e.key == 'ArrowUp' || e.key == 'ArrowDown')) {
7428         e.stopPropagation();
7429         e.preventDefault();
7430         this.dotNetRef.invokeMethodAsync('OnKeyDownAsync', { key: e.key });
7431       } else if (e.key != 'Enter' && e.key != 'Escape' && e.key != 'Tab' && e.key != 'F6') {
7432         e.stopPropagation();
7433       }
7434    }
7435  };
7436
7437  onBlur = (e) => {
7438    if (e.relatedTarget && e.relatedTarget.matches('.rz-spreadsheet-editor-input') && e.relatedTarget.closest('.rz-spreadsheet') == this.element.closest('.rz-spreadsheet')) {
7439      return;
7440    }
7441
7442    this.dotNetRef.invokeMethodAsync('OnBlurAsync');
7443  }
7444
7445  onFocus = () => {
7446    this.dotNetRef.invokeMethodAsync('OnFocusAsync');
7447  }
7448
7449  focus() {
7450    this.element.focus();
7451    const range = document.createRange();
7452    range.selectNodeContents(this.element);
7453    range.collapse(false);
7454
7455    const selection = getSelection();
7456    selection.removeAllRanges();
7457    selection.addRange(range);
7458  }
7459
7460  dispose() {
7461    this.element.removeEventListener('input', this.onInput);
7462    this.element.removeEventListener('keydown', this.onKeyDown);
7463    this.element.removeEventListener('blur', this.onBlur);
7464    this.element.removeEventListener('focus', this.onFocus);
7465    this.element.removeEventListener('paste', this.onPaste);
7466    document.removeEventListener('selectionchange', this.onSelectionChange);
7467  }
7468}
7469
7470
7471
7472
7473Radzen.createSheetEditor = (element, value, autoFocus, dotNetRef) => new SheetEditor(element, value, autoFocus, dotNetRef);
7474Radzen.createSpreadsheet = (element, dotNetRef, shortcuts) => new Spreadsheet(element, dotNetRef, shortcuts);
7475Radzen.createVirtualItemContainer = (scrollable, content, ref) => {
7476  var height = scrollable.clientHeight;
7477  var width = scrollable.clientWidth;
7478  var scrollHeight = scrollable.scrollHeight;
7479  var scrollWidth = scrollable.scrollWidth;
7480
7481  content.addEventListener('mousewheel', function (e) {
7482    scrollable.scrollBy({
7483      top: e.deltaY,
7484      left: e.deltaX
7485    });
7486    e.preventDefault();
7487  });
7488
7489  var rtl = Radzen.isRTL(scrollable);
7490
7491  var inflight = false;
7492
7493  function notifyScroll() {
7494    var scrollTop = scrollable.scrollTop;
7495    // In RTL the native scrollLeft is 0 at the right and negative toward the left;
7496    // report a non-negative logical scroll so the C# layout math stays direction-agnostic.
7497    var scrollLeft = rtl ? -scrollable.scrollLeft : scrollable.scrollLeft;
7498
7499    inflight = true;
7500
7501    ref.invokeMethodAsync('OnScroll', scrollLeft, scrollTop).finally(function () {
7502      inflight = false;
7503
7504      if (scrollTop !== scrollable.scrollTop ||
7505          scrollLeft !== (rtl ? -scrollable.scrollLeft : scrollable.scrollLeft)) {
7506        notifyScroll();
7507      }
7508    });
7509  }
7510
7511  scrollable.addEventListener('scroll', function () {
7512    if (!inflight) {
7513      notifyScroll();
7514    }
7515  });
7516
7517  var observer = new ResizeObserver(function () {
7518    const height = scrollable.clientHeight;
7519    const width = scrollable.clientWidth;
7520
7521    ref.invokeMethodAsync('OnResize', width, height);
7522  });
7523
7524  observer.observe(scrollable);
7525
7526  return { width, height, scrollHeight, scrollWidth };
7527};
7528Radzen.scrollElementTo = (scrollable, left, top) => {
7529  scrollable.scrollTo({ top: top, left: Radzen.isRTL(scrollable) ? -left : left });
7530};
7531Radzen.createFormField = function(el) {
7532  if (!el || typeof el.addEventListener !== 'function') return { dispose: function() {} };
7533  function onFocusIn() { el.classList.add('rz-state-focused'); }
7534  function onFocusOut() { el.classList.remove('rz-state-focused'); }
7535  el.addEventListener('focusin', onFocusIn);
7536  el.addEventListener('focusout', onFocusOut);
7537  return { dispose: function() {
7538    el.removeEventListener('focusin', onFocusIn);
7539    el.removeEventListener('focusout', onFocusOut);
7540  }};
7541};
7542Radzen.createSignaturePad = function(element, ref, strokeColor, strokeWidth, disabled, initialValue) {
7543  var noop = { update: function() {}, clear: function() {}, dispose: function() {} };
7544  if (!element) return noop;
7545
7546  var canvas = element.querySelector('canvas');
7547  if (!canvas) return noop;
7548
7549  var ctx = canvas.getContext('2d');
7550  var drawing = false;
7551  var paths = [];
7552  var currentPath = [];
7553
7554  function syncSize() {
7555    var rect = canvas.getBoundingClientRect();
7556    var w = Math.round(rect.width);
7557    var h = Math.round(rect.height);
7558    if (w > 0 && h > 0 && (canvas.width !== w || canvas.height !== h)) {
7559      canvas.width = w;
7560      canvas.height = h;
7561      redraw();
7562    }
7563  }
7564
7565  function getPointerPos(e) {
7566    var rect = canvas.getBoundingClientRect();
7567    var scaleX = canvas.width / rect.width;
7568    var scaleY = canvas.height / rect.height;
7569    return {
7570      x: (e.clientX - rect.left) * scaleX,
7571      y: (e.clientY - rect.top) * scaleY
7572    };
7573  }
7574
7575  function redraw() {
7576    ctx.clearRect(0, 0, canvas.width, canvas.height);
7577    ctx.strokeStyle = strokeColor;
7578    ctx.lineWidth = strokeWidth;
7579    ctx.lineCap = 'round';
7580    ctx.lineJoin = 'round';
7581    for (var i = 0; i < paths.length; i++) {
7582      var path = paths[i];
7583      if (path.length < 2) continue;
7584      ctx.beginPath();
7585      ctx.moveTo(path[0].x, path[0].y);
7586      for (var j = 1; j < path.length; j++) {
7587        ctx.lineTo(path[j].x, path[j].y);
7588      }
7589      ctx.stroke();
7590    }
7591  }
7592
7593  function loadImage(dataUrl) {
7594    if (!dataUrl) return;
7595    var img = new Image();
7596    img.onload = function() {
7597      ctx.clearRect(0, 0, canvas.width, canvas.height);
7598      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
7599    };
7600    img.src = dataUrl;
7601  }
7602
7603  syncSize();
7604
7605  if (initialValue) {
7606    loadImage(initialValue);
7607  }
7608
7609  var resizeObserver = new ResizeObserver(function() { syncSize(); });
7610  resizeObserver.observe(canvas);
7611
7612  function onPointerDown(e) {
7613    if (disabled) return;
7614    drawing = true;
7615    currentPath = [getPointerPos(e)];
7616    canvas.setPointerCapture(e.pointerId);
7617    e.preventDefault();
7618  }
7619
7620  function onPointerMove(e) {
7621    if (!drawing || disabled) return;
7622    var pos = getPointerPos(e);
7623    currentPath.push(pos);
7624
7625    ctx.strokeStyle = strokeColor;
7626    ctx.lineWidth = strokeWidth;
7627    ctx.lineCap = 'round';
7628    ctx.lineJoin = 'round';
7629    if (currentPath.length >= 2) {
7630      ctx.beginPath();
7631      ctx.moveTo(currentPath[currentPath.length - 2].x, currentPath[currentPath.length - 2].y);
7632      ctx.lineTo(pos.x, pos.y);
7633      ctx.stroke();
7634    }
7635    e.preventDefault();
7636  }
7637
7638  function onPointerUp(e) {
7639    if (!drawing || disabled) return;
7640    drawing = false;
7641    if (currentPath.length > 0) {
7642      paths.push(currentPath);
7643      currentPath = [];
7644    }
7645    try { suppressDisposed(ref.invokeMethodAsync('RadzenSignaturePad.OnStrokeEnd', canvas.toDataURL('image/png'))); } catch (ex) { }
7646    e.preventDefault();
7647  }
7648
7649  canvas.addEventListener('pointerdown', onPointerDown);
7650  canvas.addEventListener('pointermove', onPointerMove);
7651  canvas.addEventListener('pointerup', onPointerUp);
7652  canvas.addEventListener('pointercancel', onPointerUp);
7653  canvas.style.touchAction = 'none';
7654
7655  return {
7656    update: function(newStrokeColor, newStrokeWidth, newDisabled) {
7657      strokeColor = newStrokeColor;
7658      strokeWidth = newStrokeWidth;
7659      disabled = newDisabled;
7660      redraw();
7661    },
7662    clear: function() {
7663      paths = [];
7664      currentPath = [];
7665      ctx.clearRect(0, 0, canvas.width, canvas.height);
7666    },
7667    dispose: function() {
7668      resizeObserver.disconnect();
7669      canvas.removeEventListener('pointerdown', onPointerDown);
7670      canvas.removeEventListener('pointermove', onPointerMove);
7671      canvas.removeEventListener('pointerup', onPointerUp);
7672      canvas.removeEventListener('pointercancel', onPointerUp);
7673    }
7674  };
7675};
7676Radzen.itemListKeydown = function (e) {
7677  var el = e.target;
7678  if (!el || !el.classList) return;
7679  var radio = el.classList.contains('rz-radio-button-list');
7680  var checkbox = el.classList.contains('rz-checkbox-list');
7681  if (!radio && !checkbox) return;
7682  var key = e.code ? e.code : e.key;
7683  if (key === 'ArrowLeft' || key === 'ArrowRight' || key === 'ArrowUp' || key === 'ArrowDown') {
7684    var horizontal = key === 'ArrowLeft' || key === 'ArrowRight';
7685    if (radio || el.classList.contains(horizontal ? 'rz-checkbox-list-horizontal' : 'rz-checkbox-list-vertical')) {
7686      e.preventDefault();
7687    }
7688  } else if (key === 'Home' || key === 'End' || key === 'Space' || key === 'Enter') {
7689    e.preventDefault();
7690  }
7691};
7692document.addEventListener('keydown', Radzen.itemListKeydown);
7693Radzen.chatKeydown = function (e) {
7694  var el = e.target;
7695  if (!el || !el.classList || !el.classList.contains('rz-chat-textarea')) return;
7696  var container = el.closest('.rz-chat-input');
7697  var popupOpen = container && container.querySelector('.rz-chat-mention-popup');
7698  if (e.key === 'Enter') {
7699    if (!e.shiftKey || popupOpen) {
7700      e.preventDefault();
7701    }
7702  } else if (popupOpen && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
7703    e.preventDefault();
7704  } else if ((e.key === 'Backspace' || e.key === 'Delete') && el.dataset.mentionSegments) {
7705    var start = el.selectionStart;
7706    var end = el.selectionEnd;
7707    var segments = el.dataset.mentionSegments.split(',');
7708    for (var i = 0; i < segments.length; i++) {
7709      var parts = segments[i].split(':');
7710      var segmentStart = parseInt(parts[0], 10);
7711      var segmentEnd = segmentStart + parseInt(parts[1], 10);
7712      var hit;
7713      if (start !== end) {
7714        hit = segmentStart < end && segmentEnd > start;
7715      } else {
7716        var target = e.key === 'Backspace' ? start - 1 : start;
7717        hit = target >= 0 && segmentStart <= target && segmentEnd > target;
7718      }
7719      if (hit) {
7720        e.preventDefault();
7721        break;
7722      }
7723    }
7724  }
7725};
7726document.addEventListener('keydown', Radzen.chatKeydown);
7727Radzen.popupTriggerKeydown = function (e) {
7728  if (e.altKey || e.ctrlKey || e.metaKey) return;
7729  var el = e.target;
7730  if (!el || !el.classList) return;
7731  var key = e.code ? e.code : e.key;
7732  var vertical = key === 'ArrowUp' || key === 'ArrowDown';
7733  if (el.classList.contains('rz-splitbutton-menubutton')) {
7734    if (vertical) e.preventDefault();
7735  } else if (el.classList.contains('rz-menu-list') || el.classList.contains('rz-navigation-menu')) {
7736    if (vertical || key === 'Home' || key === 'End' || key === 'Space') e.preventDefault();
7737  } else if (el.classList.contains('rz-navigation-item-wrapper') && el.getAttribute('aria-haspopup')) {
7738    if (vertical || key === 'Home' || key === 'End' || key === 'Space') e.preventDefault();
7739  } else if (el.classList.contains('rz-dropdown') || el.classList.contains('rz-listbox')) {
7740    if (vertical || key === 'ArrowLeft' || key === 'ArrowRight' || key === 'Space') e.preventDefault();
7741  } else if (el.classList.contains('rz-colorpicker')) {
7742    if (key === 'Space') e.preventDefault();
7743  }
7744};
7745document.addEventListener('keydown', Radzen.popupTriggerKeydown);
7746Radzen.datePickerKeydown = function (e) {
7747  if (e.isComposing) return;
7748  var el = e.target;
7749  if (!el || el.tagName !== 'INPUT' || !el.classList || !el.classList.contains('rz-inputtext') || !el.closest('.rz-datepicker')) return;
7750  var key = e.code ? e.code : e.key;
7751  if (key === 'Enter' || (e.altKey && key === 'ArrowDown')) {
7752    e.preventDefault();
7753  }
7754};
7755document.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.