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