1window.scrollToBottom = (elementOrId) => { 2 if (!elementOrId) return; 3 const el = (typeof elementOrId === 'string') 4 ? document.getElementById(elementOrId) 5 : elementOrId; 6 if (el && el.scrollTop !== undefined) { 7 el.scrollTop = el.scrollHeight; 8 } 9}; 10 11// Saves the current scroll height of the element so we can restore relative position after prepending content. 12window.argusGetScrollHeight = (el) => el ? el.scrollHeight : 0; 13 14// Adjusts scrollTop so the viewport stays on the same content after new items were prepended above. 15window.argusRestoreScrollAfterPrepend = (el, scrollHeightBefore) => { 16 if (!el) return; 17 el.scrollTop = el.scrollHeight - scrollHeightBefore; 18}; 19 20// Auto-expands a textarea from 1 row up to ~4 rows, then scrolls internally. 21window.argusAutoExpandTextarea = (el) => { 22 if (!el) return; 23 el.style.height = 'auto'; 24 const lineHeight = parseInt(getComputedStyle(el).lineHeight) || 20; 25 const maxHeight = lineHeight * 4 + parseInt(getComputedStyle(el).paddingTop) * 2; 26 el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden'; 27 el.style.height = Math.min(el.scrollHeight, maxHeight) + 'px'; 28}; 29 30// Resets textarea back to single-row height after message is sent. 31window.argusResetTextarea = (el) => { 32 if (!el) return; 33 el.value = ''; // clear DOM value immediately (Blazor will sync on next render) 34 el.style.height = 'auto'; 35 el.style.overflowY = 'hidden'; 36}; 37 38// Attaches a native keydown listener to the chat textarea so Enter can be 39// intercepted with preventDefault() before the browser inserts a newline. 40// dotNetRef must expose [JSInvokable] OnEnterPressed(). 41window.argusInitChatTextarea = (el, dotNetRef) => { 42 if (!el || !dotNetRef) return; 43 el._chatKeyHandler = (e) => { 44 if (e.key === 'Enter' && !e.shiftKey) { 45 e.preventDefault(); 46 dotNetRef.invokeMethodAsync('OnEnterPressed'); 47 } 48 }; 49 el.addEventListener('keydown', el._chatKeyHandler); 50}; 51 52// Pins the chat page to fill from below the last header bar to the bottom of the viewport. 53// Uses position:fixed on the chat wrapper, and a sentinel div to measure where content starts. 54window.argusChatFillHeight = (sentinel, chatPage) => { 55 const apply = () => { 56 if (!sentinel) return; 57 const top = sentinel.getBoundingClientRect().top; 58 document.documentElement.style.setProperty('--argus-chat-top', Math.round(top) + 'px'); 59 }; 60 requestAnimationFrame(() => { 61 apply(); 62 // Second pass after a short delay handles any deferred Blazor/theme renders 63 setTimeout(apply, 300); 64 }); 65 window.addEventListener('resize', apply, { passive: true }); 66}; 67 68window.AgTextBox = (function () { 69 'use strict'; 70 71 // Map of elementId -> { element: HTMLInputElement, handler: Function } 72 const handlers = new Map(); 73 74 // Returns 0=neutral, 1=Greek, 2=Latin for a single character 75 function getAlphabetMode(ch) { 76 const code = ch.codePointAt(0);
77 if (code === undefined) return 0; 78 79 // Greek Extended: U+0370-U+03FF (Greek and Coptic), U+1F00-U+1FFF (Greek Extended) 80 if ((code >= 0x0370 && code <= 0x03FF) || (code >= 0x1F00 && code <= 0x1FFF)) 81 return 1; 82 83 // Latin: Basic Latin A-Z/a-z + Latin-1 Supplement + Latin Extended A/B + IPA Extensions + Latin Extended Additional 84 if ((code >= 0x0041 && code <= 0x005A) || 85 (code >= 0x0061 && code <= 0x007A) || 86 (code >= 0x00C0 && code <= 0x00FF) || 87 (code >= 0x0100 && code <= 0x017F) || 88 (code >= 0x0180 && code <= 0x024F) || 89 (code >= 0x1E00 && code <= 0x1EFF)) 90 return 2; 91 92 return 0; // digits, punctuation, spaces, other scripts â neutral 93 } 94 95 // Mirrors C# RemoveMixedGreekLatin. Also tracks cursor position through the transformation. 96 function removeMixedGreekLatin(input, cursorPos) { 97 if (!input) return { value: input, cursor: cursorPos }; 98 99 let selectedAlphabet = 0; 100 let result = ''; 101 let newCursor = 0; 102 103 for (let i = 0; i < input.length; i++) { 104 const ch = input[i]; 105 const mode = getAlphabetMode(ch); 106 107 let include; 108 if (mode === 0) { 109 include = true;
109 // neutral chars (digits, spaces, etc.) always pass 110 } else if (selectedAlphabet === 0) { 111 selectedAlphabet = mode; // lock alphabet on first letter 112 include = true; 113 } else { 114 include = mode === selectedAlphabet; // drop letters from the other alphabet 115 } 116 117 if (include) { 118 result += ch; 119 if (i < cursorPos) newCursor++; // keep cursor aligned with surviving chars 120 } 121 // if excluded: newCursor doesn't advance â cursor stays in place 122 } 123 124 return { value: result, cursor: Math.min(newCursor, result.length) }; 125 } 126 127 // Mirrors C# Conv.ToUpperGreekSafe: uppercase + strip diacritics (NFD â remove Mn marks â NFC) 128 function toUpperGreekSafe(input) { 129 if (!input) return input; 130 return input 131 .toUpperCase() 132 .normalize('NFD') 133 .replace(/\p{Mn}/gu, '') // strip all combining / nonspacing marks (accents, tonos, etc.) 134 .normalize('NFC'); 135 } 136 137 // Patch the instance's value property so that any programmatic write 138 // (Telerik/Blazor re-render) restores the cursor when the field is focused. 139 // We override on the instance only, prototype is untouched. 140 function patchInputCursor(input) { 141 if (input._agCursorPatched) return; 142 143 const proto = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value'); 144 145 Object.defineProperty(input, 'value', { 146 get: proto.get, 147 set: function (v) { 148 149 if (v == null) v = ''; 150 151 const focused = document.activeElement === this; 152 const start = focused ? this.selectionStart : -1; 153 const end = focused ? this.selectionEnd : -1; 154 proto.set.call(this, v); 155 if (focused && start >= 0) { 156 this.setSelectionRange( 157 Math.min(start, v.length), 158 Math.min(end, v.length) 159 ); 160 } 161 }, 162 configurable: true 163 }); 164 165 input._agCursorPatched = true; 166 } 167 168 function unpatchInputCursor(input) { 169 if (!input._agCursorPatched) return; 170 delete input.value; // removes own property â prototype takes over again 171 delete input._agCursorPatched; 172 } 173 174 function createHandler(multiLanguageOff, capitalize) { 175 let processing = false; // re-entrancy guard for the synthetic event we dispatch 176 177 return function (e) { 178 if (processing) return; 179 180 const el = e.target; 181 const cursorPos = el.selectionStart; 182 let value = el.value; 183 let newCursor = cursorPos; 184 185 if (multiLanguageOff) { 186 const r = removeMixedGreekLatin(value, cursorPos); 187 value = r.value; 188 newCursor = r.cursor; 189 } 190 191 if (capitalize) { 192 // toUpperGreekSafe is effectively length-preserving for NFC Greek/Latin input 193 // (each accented NFC char â one unaccented NFC char), so cursor stays valid 194 value = toUpperGreekSafe(value); 195 } 196 197 if (value !== el.value) { 198 processing = true; 199 el.value = value; 200 el.setSelectionRange(newCursor, newCursor); 201 // Dispatch a new input event so Telerik/Blazor picks up the corrected value 202 el.dispatchEvent(new InputEvent('input', { bubbles: true, cancelable: false })); 203 processing = false; 204 } 205 }; 206 } 207 208 return { 209 /** 210 * Attach a client-side input handler to the element with the given id. 211 * @param {string} id - The id attribute of the <input> element (matches TelerikTextBox Id param) 212 * @param {boolean} multiLanguageOff - true when MultiLanguage=false (restrict to first-seen alphabet) 213 * @param {boolean} capitalize - true when Capitalize=true (uppercase + strip accents) 214 */ 215 init: function (id, multiLanguageOff, capitalize) { 216 this.dispose(id); // clean up any previous handler for this id 217 218 const el = document.getElementById(id); 219 if (!el) return; 220 221 // Telerik renders the <input> directly with the given id, but guard against wrappers 222 const input = el.tagName === 'INPUT' ? el : el.querySelector('input'); 223 if (!input) return; 224 225 // Always patch, prevents Blazor/Telerik re-renders from mov
225ing the cursor, 226 // even when no text transformation is configured (e.g. inside EditForms). 227 patchInputCursor(input); 228 229 let handler = null; 230 if (multiLanguageOff || capitalize) { 231 handler = createHandler(multiLanguageOff, capitalize); 232 input.addEventListener('input', handler); 233 } 234 235 handlers.set(id, { element: input, handler: handler }); 236 }, 237 238 dispose: function (id) { 239 const entry = handlers.get(id); 240 if (entry) { 241 if (entry.handler) 242 entry.element.removeEventListener('input', entry.handler); 243 unpatchInputCursor(entry.element); 244 handlers.delete(id); 245 } 246 } 247 }; 248})(); 249 250window.argusDownloadFile = (url, fileName) => { 251 const a = document.createElement('a'); 252 a.href = url; 253 a.download = fileName || ''; 254 a.style.display = 'none'; 255 document.body.appendChild(a); 256 a.click(); 257 document.body.removeChild(a); 258}; 259
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.