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