PageSourceSearch

https://ops.iris.edu.gr/_content/Radzen.Blazor/Radzen.Blazor.js?v=b4Mzb_mN8KDABVjKZVbj72EFiXDEn__aA6kUFrMwGT0

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

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.