1 2function getUserDeviceInfo() { 3 var parser = new UAParser(); 4 var result = parser.getResult(); 5 return { 6 operatingSystem: result.os.name, 7 operatingSystemVersion: result.os.version, 8 browserInfo: result.browser.name + ' ' + result.browser.version 9 }; 10} 11 12function updateLanguage(lang) { 13 document.querySelector('html').setAttribute('lang', lang.cultureCode); 14} 15 16function scrollToBottom(selector) { 17 let element = document.querySelector(`.${selector}`); 18 if (element) { 19 element.scrollTop = element.scrollHeight; 20 } 21} 22 23function scrollToTop(selector) { 24 let element = document.querySelector(`.${selector}`); 25 if(element) { 26 element.scrollTop = 0; 27 } 28} 29 30function temporarilyDisableAutoHideForContextMenu(contextMenuId) { 31 const element = document.getElementById(contextMenuId); 32 if (element) { 33 element.dataset.autohide = 'false'; 34 setTimeout(function () { 35 element.dataset.autohide = 'true'; 36 }, 300); 37 } 38} 39 40function clickById(id){ 41 document.getElementById(id).click(); 42} 43 44function clearFileSelector(){ 45 document.querySelector(".mud-file-upload input[type='file']").value= null; 46} 47 48function trackPageView(url) { 49 gtag('event', 'page_view', { page_path: url }); 50} 51 52function attachInputValidation(wrapper, allowedPattern, maxLength) { 53 // Store pattern/maxLength on wrapper so they can be updated dynamically 54 // (e.g. when backend selection changes the allowed character set). 55 wrapper.dataset.allowedPattern = allowedPattern; 56 wrapper.dataset.maxLength = String(maxLength); 57 58 function sanitize(field) { 59 const pattern = new RegExp(`[^${wrapper.dataset.allowedPattern}]`, 'g'); 60 const ml = parseInt(wrapper.dataset.maxLength) || 0; 61 let sanitized = field.value.replace(pattern, ''); 62 if (ml > 0) sanitized = sanitized.slice(0, ml); 63 if (sanitized !== field.value) { 64 field.value = sanitized; 65 field.dispatchEvent(new Event('input', { bubbles: true })); 66 } 67 } 68 69 function attachToInput(field) { 70 if (field.dataset.validationAttached) return; 71 field.dataset.validationAttached = 'true'; 72 field.addEventListener('input', (event) => { 73 if (!event.isTrusted) return; // Ignore our own synthetic events 74 sanitize(field); 75 }); 76 } 77 78 const existing = wrapper.querySelector('input'); 79 if (existing) attachToInput(existing); 80 81 // Guard: only create one MutationObserver per wrapper. 82 if (wrapper.dataset.observerAttached) return; 83 wrapper.dataset.observerAttached = 'true'; 84 85 // Re-attach when @key causes MudTextField to be recreated inside the wrapper. 86 new MutationObserver(() => { 87 const input = wrapper.querySelector('input'); 88 if (input) attachToInput(input); 89 }).observe(wrapper, { childList: true, subtree: true }); 90} 91 92function attachPasteHandler(elementId, dotNetHelper) { 93 const field = document.getElementById(elementId); 94 if (field) { 95 field.addEventListener('paste', async (event) => { 96 event.preventDefault(); 97 const clipboardData = event.clipboardData || window.clipboardData; 98 const pastedData = clipboardData.getData('Text'); 99 100 // Invoke the C# method and pass the pasted data 101 await dotNetHelper.invokeMethodAsync('OnPasteReceived', pastedData, field.id); 102 }); 103 } 104} 105 106function getElementPosition (element) { 107 if (!element) { 108 return null; 109 } 110 const rect = element.getBoundingClientRect(); 111 return { 112 top: rect.top, 113 left: rect.left, 114 right: rect.right, 115 bottom: rect.bottom, 116 width: rect.width, 117 height: rect.height, 118 windowScrollX: window.scrollX, 119 windowScrollY: window.scrollY 120 }; 121} 122 123function getElementPositionWithinScrollableParent (element, parent) { 124 if (!element || !parent) return null; 125 126 const elementRect = element.getBoundingClientRect(); 127 const parentRect = parent.getBoundingClientRect(); 128 129 return { 130 top: elementRect.top - parentRect.top + parent.scrollTop, 131 left: elementRect.left - parentRect.left + parent.scrollLeft, 132 width: elementRect.width, 133 height: elementRect.height 134 }; 135} 136 137function adjustContextMenuPositionInScrollableContainer(contextMenuId, sourceDiv, containerElement) { 138 const menuElement = document.getElementById(contextMenuId); 139 if (!menuElement || !containerElement || !sourceDiv) { 140 console.warn("Menu, container, or source element not found."); 141 return; 142 } 143 144 const containerRect = containerElement.getBoundingClientRect(); 145 const menuRect = menuElement.getBoundingClientRect(); 146 const sourceWithin = getElementPositionWithinScrollableParent(sourceDiv, containerElement); // {top,left,width,height} 147 if (!sourceWithin) return; 148 149 // Current menu position converted to container scroll space 150 const menuLeftInContainer = menuRect.left - containerRect.left + containerElement.scrollLeft; 151 const menuTopInContainer = menuRect.top - containerRect.top + containerElement.scrollTop; 152 153 let newLeft = menuLeftInContainer; 154 let newTop = menuTopInContainer; 155 156 // --- Horizontal: flip left if the menu overflows the container's right edge (viewport comparison is OK) --- 157 if (menuRect.right > containerRect.right) { 158 // Place the menu to the left of the trigger, in container space
159 newLeft = sourceWithin.left - menuRect.width; 160 } 161 162 // Clamp horizontally to the visible area of the container 163 const minLeft = containerElement.scrollLeft; 164 const maxLeft = containerElement.scrollLeft + containerElement.clientWidth - menuRect.width; 165 newLeft = Math.min(Math.max(newLeft, minLeft), Math.max(minLeft, maxLeft)); 166 167 // --- Vertical: align the menu just above the trigger in container space to avoid "creeping" --- 168 const desiredTop = sourceWithin.top - menuRect.height; 169 170 // If it's drifted above the desired anchor OR above the visible top, snap back 171 if (menuTopInContainer < desiredTop || menuRect.top < containerRect.top) { 172 newTop = desiredTop; 173 } 174 175 // Clamp vertically to the visible area of the container 176 const minTop = containerElement.scrollTop; 177 const maxTop = containerElement.scrollTop + containerElement.clientHeight - menuRect.height; 178 newTop = Math.min(Math.max(newTop, minTop), Math.max(minTop, maxTop)); 179 180 // Apply numbers in the same coordinate system the menu was initially shown with (container scroll space) 181 menuElement.style.left = `${Math.round(newLeft)}px`; 182 menuElement.style.top = `${Math.round(newTop)}px`; 183} 184 185function adjustContextMenuPositionOnPage(contextMenuId, sourceDiv) { 186 const menuElement = document.getElementById(contextMenuId); 187 if (!menuElement) { 188 console.warn("Menu element not found."); 189 return; 190 } 191 const menuRect = menuElement.getBoundingClientRect(); 192 const sourceDivRect = sourceDiv.getBoundingClientRect(); 193 194 let adjustedY = menuRect.top; 195 var elementPosition = getElementPosition(sourceDiv); 196 197 // The component does not take into account the scrolling of the page/container, 198 // therefore on multiple openings of the contextmenu it will jump further up until it reaches the point where it fits into the unscrolled page area 199 if (parseInt(menuElement.style.top, 10) < sourceDivRect.top+elementPosition.windowScrollY-menuRect.height) { 200 adjustedY = sourceDivRect.top + elementPosition.windowScrollY - menuRect.height; 201 menuElement.style.top = `${adjustedY}px`; 202 } 203 204} 205 206function createPdfUrl(bytes) { 207 const blob = new Blob([new Uint8Array(bytes)], { type: "application/pdf" }); 208 return URL.createObjectURL(blob); 209}
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.