1/*eslint eqeqeq: ["error", "smart"], no-extra-parens: off */ 2 3function parseDate(data) { 4 if (data instanceof Date) { return data; } 5 if (data == "") { return new Date(); } 6 7 const d = new Date(0); 8 if (data === null || data === undefined) { return d; } 9 10 let milliseconds = 0; 11 12 //if data is formatted as "/Date(1234567890)/" then parse it 13 if (data.startsWith("/Date(")) { 14 milliseconds = data.substring(6, data.length - 2); 15 } 16 else { 17 milliseconds = Date.parse(data); 18 } 19 20 milliseconds = Math.max(0, milliseconds); 21 d.setUTCMilliseconds(Number.parseInt(milliseconds)); 22 23 return (d); 24} 25 26function parseBool(data) { 27 if (data === undefined) { return false; } 28 switch (data.toString().toLowerCase()) { 29 case "1": 30 case "on": 31 case "yes": 32 case "y": 33 case "true": 34 return true; 35 case "0": 36 case "off": 37 case "no": 38 case "n": 39 case "false": 40 return false; 41 default: 42 return !!data; 43 } 44} 45 46async function sleepAsync(ms) { 47 return new Promise(resolve => setTimeout(resolve, ms)); 48} 49 50function insertAtCaret(text = '') { 51 if (this.selectionStart || this.selectionStart === 0) { 52 const startPos = this.selectionStart; 53 const endPos = this.selectionEnd; 54 this.value = this.value.substring(0, startPos) + 55 text + 56 this.value.substring(endPos, this.value.length); 57 this.selectionStart = startPos + text.length; 58 this.selectionEnd = startPos + text.length; 59 } else { 60 this.value += text; 61 } 62 $(this).trigger('change'); 63 return this; 64} 65 66HTMLInputElement.prototype.insertAtCaret = insertAtCaret; 67HTMLTextAreaElement.prototype.insertAtCaret = insertAtCaret; 68 69String.prototype.pad = function (size) { 70 let s = String(this); 71 if (typeof (size) !== "number") { size = 2; } 72 73 while (s.length < size) { s = "0" + s; } 74 return s; 75}; 76 77Number.prototype.pad = String.prototype.pad; 78 79String.prototype.deCamelCase = function () { 80 return this.replaceAll(/([a-z])([A-Z]+$)|([A-Z]+)([A-Z])|([a-z])([A-Z])/g, '$1$3$5 $2$4$6'); 81}; 82 83String.prototype.escapeRegExp = function () { 84 const s = String(this); 85 return s.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string 86}; 87 88Date.prototype.getTimestamp = function () { 89 const lang = window.navigator.userLanguage || window.navigator.language; 90 const options = { 91 year: "numeric", 92 month: "2-digit", 93 day: "2-digit", 94 timeZone: 'UTC' 95 }; 96 97 return (this.toLocaleDateString(lang, options) + " " + this.toLocaleTimeString(lang)); 98}; 99 100Date.prototype.getDate = function () { 101 const lang = window.navigator.userLanguage || window.navigator.language; 102 const options = { 103 year: "numeric", 104 month: "2-digit", 105 day: "2-digit" 106 }; 107 108 return (this.toLocaleDateString(lang, options)); 109}; 110 111Date.prototype.get24hTime = function () { 112 const lang = window.navigator.userLanguage || window.navigator.language; 113 const options = { 114 hour12: false 115 }; 116 117 return (this.toLocaleTimeString(lang, options)); 118}; 119 120let getParamValue = (function () { 121 let params; 122 const resetParams = function () { 123 const query = window.location.search; 124 const regex = /[?&;](.+?)=([^&;]+)/g; 125 let match; 126 127 params = {}; 128 129 if (query) { 130 while ((match = regex.exec(query)) != null) { 131 params[match[1]] = decodeURIComponent(match[2]); 132 } 133 } 134 }; 135 136 window.addEventListener && window.addEventListener('popstate', resetParams); 137 138 resetParams(); 139 140 return function (param) { 141 return Object.hasOwn(params, param) ? params[param] : null; 142 }; 143})(); 144 145if (!Array.prototype.last) { Array.prototype.last = function () { return this.length < 1 ? null : this.at(-1); }; } 146if (!Array.prototype.first) { Array.prototype.first = function () { return this.length < 1 ? null : this[0]; }; } 147 148String.prototype.contains = function (contains) { return this.includes(contains); }; 149Array.prototype.contains = function (contains) { return this.includes(contains); }; 150String.prototype.isEmptyOrWhitespace = function () { return this.match(/^\s*$/); }; 151 152String.prototype.format = function (args) { 153 return this.replaceAll(/\{(\w+)\}/g, function (match, key) { 154 return args[key] !== undefined ? args[key] : match; 155 });
156}; 157 158Array.prototype.groupBy = function (keySelector) { 159 return this.reduce((result, currentValue) => { 160 const key = keySelector(currentValue); 161 if (!result[key]) { 162 result[key] = []; 163 } 164 result[key].push(currentValue); 165 return result; 166 }, {}); 167}; 168 169Array.prototype.distinct = function (keySelector) { 170 const seen = new Set(); 171 return this.filter(item => { 172 const key = keySelector(item); 173 if (seen.has(key)) { 174 return false; 175 } 176 seen.add(key); 177 return true; 178 }); 179}; 180 181ko.bindingHandlers.enterPressed = { 182 init: function (element, valueAccessor, _allBindings, viewModel) { 183 const callback = valueAccessor(); 184 $(element).keypress(function (event) { 185 const keyCode = (event.which ? event.which : event.keyCode); 186 if (keyCode === 13) { 187 callback.call(viewModel); 188 return false; 189 } 190 return true; 191 }); 192 } 193}; 194 195ko.bindingHandlers.fadeVisible = { 196 init: function (element, valueAccessor) { 197 // Initially set the element to be instantly visible/hidden depending on the value 198 const value = valueAccessor(); 199 $(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable 200 }, 201 update: function (element, valueAccessor) { 202 // Whenever the value subsequently changes, slowly fade the element in or out 203 const value = valueAccessor(); 204 ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut(); 205 } 206}; 207 208ko.bindingHandlers.forcePush = { 209 init: function (element, valueAccessor, _allBindings, viewModel) { 210 const callback = valueAccessor(); 211 Pressure.set(element, { 212 startDeepPress: function (event) { 213 callback.call(viewModel, element, event); 214 }, 215 }); 216 }, 217}; 218 219ko.bindingHandlers.element = { 220 init: function (element, valueAccessor) { 221 const target = valueAccessor(); 222 target(element); 223 } 224}; 225 226ko.bindingHandlers.scrollLoadMore = { 227 init: function (element, valueAccessor) { 228 const options = valueAccessor(); 229 const callback = options.callback; 230 const threshold = options.threshold || 100; 231 232 function onScroll() { 233 const scrollTop = element.scrollTop; 234 const scrollHeight = element.scrollHeight; 235 const offsetHeight = element.offsetHeight; 236 const distanceFromBottom = scrollHeight - (scrollTop + offsetHeight); 237 238 if (distanceFromBottom <= threshold) { 239 callback(); 240 } 241 } 242 243 element.addEventListener('scroll', onScroll); 244 } 245}; 246 247function getForm(selector) { 248 const form = {}; 249 250 $(selector).find('[data-formfield]').each(function () { 251 const self = $(this); 252 const name = self.data("formfield"); 253 if (self.text() != null) 254 { 255 form[name] = self.text(); 256 } 257 }); 258 259 $(selector).find(':input[name]:enabled, :input[data-formfield]:enabled').each(function () { 260 const self = $(this); 261 const name = self.attr('name') || self.data("formfield"); 262 263 if (self.is(":checkbox") || self.is(":radio")) { 264 form[name] = self.is(":checked"); 265 return; //Logic: Behaves as continue since we're in a callback 266 } 267 268 if (form[name] !== "" && form[name] != null) { 269 form[name] = form[name] + ',' + self.val(); 270 } else { 271 form[name] = self.val(); 272 } 273 }); 274 275 return form; 276} 277 278function setForm(selector, data) { 279 $(selector).find('[data-formfield]').each(function () { 280 const self = $(this); 281 const name = self.data("formfield"); 282 283 if (data[name] != null) { 284 const fieldType = self.data("formfieldtype"); 285 if (fieldType != null) { 286 let displayValue = data[name]; 287 switch (fieldType) { 288 case "DateTime": 289 displayValue = parseDate(displayValue).getTimestamp(); 290 break; 291 } 292 self.text(displayValue); 293 } 294 else { 295 self.text(data[name]); 296 } 297 } 298 else { 299 self.text(""); 300 } 301 });
302 303 $(selector).find(':input[name]:enabled').each(function () { 304 const self = $(this); 305 const name = self.attr('name'); 306 307 if (data[name] != null) { 308 if (self.is(":checkbox") || self.is(":radio")) { 309 self.prop("checked", data[name]); 310 return; //Logic: Behaves as continue since we're in a callback 311 } 312 313 self.val(data[name]); 314 } 315 else { 316 self.val(""); 317 } 318 }); 319} 320 321function WildcardToRegex(pattern) 322{ 323 if (pattern == null) { return null; } 324 325 let escapeRegex = function (string) { 326 return string.replaceAll(/([.*+?^${}()|[\]/\\])/g, "\\$1"); 327 }; 328 329 let escapeReplace = function (data, original, replacement) { 330 const searchRegex = new RegExp("\\\\+\\" + original, "g"); 331 const newRegex = data.replaceAll(searchRegex, function (match) { 332 const count = match.length - 1; 333 const halfCount = Math.floor(count / 2); 334 const newSlashes = new Array(halfCount).join("\\"); 335 const result = newSlashes + ((halfCount % 2 === 0) ? replacement : original); 336 return result; 337 }); 338 return newRegex; 339 }; 340 341 let toRegex = function (pattern, starMatchesEmpty, flags) { 342 let reg = "^" + escapeRegex(pattern) + "$"; 343 reg = escapeReplace(reg, "?", "(.)"); 344 reg = escapeReplace(reg, "*", starMatchesEmpty === true ? "(.*?)" : "(.+?)"); 345 return new RegExp(reg, flags); 346 }; 347 348 return toRegex(pattern, false); 349} 350 351function CalcLevenshteinDistance(a, b) { 352 if (a == null || b == null) { return 0; } 353 354 const lengthA = a.length; 355 const lengthB = b.length; 356 let distances = []; 357 for (let x = 0; x <= lengthA; x++) { 358 distances[x] = []; 359 } 360 for (let i = 0; i <= lengthA; distances[i][0] = i++); 361 for (let j = 0; j <= lengthB; distances[0][j] = j++); 362 363 for (let i = 1; i <= lengthA; i++) { 364 for (let j = 1; j <= lengthB; j++) { 365 const cost = b[j - 1] === a[i - 1] ? 0 : 1; 366 distances[i][j] = Math.min( 367 Math.min(distances[i - 1][j] + 1, distances[i][j - 1] + 1), 368 distances[i - 1][j - 1] + cost); 369 } 370 } 371 return distances[lengthA][lengthB]; 372} 373 374function uuid4() { 375 function hex(s, b) { 376 return s + 377 (b >>> 4).toString(16) + // high nibble 378 (b & 0b1111).toString(16); // low nibble 379 } 380 381 let r = crypto.getRandomValues(new Uint8Array(16)); 382 383 r[6] = r[6] >>> 4 | 0b01000000; // Set type 4: 0100 384 r[8] = r[8] >>> 3 | 0b10000000; // Set variant: 100 385 386 return r.slice(0, 4).reduce(hex, '') + 387 r.slice(4, 6).reduce(hex, '-') + 388 r.slice(6, 8).reduce(hex, '-') + 389 r.slice(8, 10).reduce(hex, '-') + 390 r.slice(10, 16).reduce(hex, '-'); 391} 392 393function generateSecurePassword(length) { 394 const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^*()_+'; 395 let password = ''; 396 const buffer = new Uint8Array(length); 397 398 crypto.getRandomValues(buffer); 399 for (const element of buffer) { 400 password += charset[element % charset.length]; 401 } 402 403 return password; 404} 405 406class Version { 407 constructor(major, minor, revision, build) { 408 this.Major = major || 0; 409 this.Minor = minor || 0; 410 this.Revision = revision || 0; 411 this.Build = build || 0; 412 } 413 414 static parse(versionString = "0.0.0.0") { 415 const [major, minor, revision, build] = versionString.split(".").map(Number); 416 return new Version(major, minor, revision, build); 417 } 418 419 static fromObject(obj) { 420 return new Version(obj?.Major || 0, obj?.Minor || 0, obj?.Revision || 0, obj?.Build || 0); 421 } 422 423 newerThan = otherVersion => this.compare(otherVersion) > 0; 424 olderThan = otherVersion => this.compare(otherVersion) < 0; 425 equalTo = otherVersion => this.compare(otherVersion) === 0; 426 olderThanOrEqualTo = otherVersion => this.compare(otherVersion) <= 0; 427 newerThanOrEqualTo = otherVersion => this.compare(otherVersion) >= 0; 428 toString = (parts) => [this.Major, this.Minor, this.Revision, this.Build].splice(0, parts || 4).join("."); 429 430 getParts(parts) { 431 return parse(toString(parts)); 432 } 433 434 compare(otherVersion) { 435 if (typeof (otherVersion) === "string") { 436 return this.compare(Version.parse(otherVersion)); 437 } 438 439 if (!(otherVersion instanceof Version)) { 440 throw new TypeError("Invalid argument: 'otherVersion' must be an instance of Version."); 441 } 442 443 if (this.Major !== otherVersion.Major) { 444 return this.Major - otherVersion.Major; 445 } 446 447 if (this.Minor !== otherVersion.Minor) { 448 return this.Minor - otherVersion.Minor; 449 } 450 451 if (this.Revision !== otherVersion.Revision) { 452 return this.Revision - otherVersion.Revision; 453 } 454 455 return this.Build - otherVersion.Build; 456 } 457} 458 459class TokenStream { 460 constructor(input) { 461 this.input = input; 462 this.index = 0; 463 this.regex = /"[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*'|==|!=|>=|<=|&&|\|\||<|>|\(|\)|\b[A-Za-z0-9_.]+\b|\btrue\b|\bfalse\b|\b\d+\b/g; 464 this.tokens = this.input.match(this.regex); 465 } 466 467 next() { return this.tokens[this.index++]; } 468 peek() { return this.tokens[this.index]; } 469 eof() { return this.peek() === undefined; } 470} 471 472class Parser { 473 constructor(input, callback) { 474 this.tokens = new TokenStream(input); 475 this.callback = callback; 476 } 477 478 parse() { 479 let expr = this.boolean(); 480 if (!this.tokens.eof()) throw new SyntaxError("Unexpected '" + this.tokens.peek() + "'"); 481 return expr; 482 } 483 484 boolean() { 485 let expr = this.comparison(); 486 while (!this.tokens.eof() && /^&&|\|\|$/.test(this.tokens.peek())) { 487 let op = this.tokens.next() === '&&' ? (a, b) => a && b : (a, b) => a || b; 488 expr = op(expr, this.comparison()); 489 } 490 return expr; 491 } 492 493 comparison() { 494 let expr = this.expression(); 495 let operators = { 496 '==': (a, b) => a == b, 497 '!=': (a, b) => a != b, 498 '>=': (a, b) => a >= b, 499 '<=': (a, b) => a <= b, 500 '>': (a, b) => a > b, 501 '<': (a, b) => a < b 502 }; 503 504 while (!this.tokens.eof() && operators[this.tokens.peek()]) { 505 let op = operators[this.tokens.next()]; 506 expr = op(expr, this.expression()); 507 } 508 509 return expr; 510 } 511 512 expression() { 513 let expr; 514 if (this.tokens.peek() === "(") { 515 this.tokens.next(); 516 expr = this.boolean(); 517 if (this.tokens.next() !== ")") throw new SyntaxError("Expected ')'"); 518 } else { 519 expr = this.value(); 520 } 521 return expr; 522 } 523
524 value() { 525 if (/^\d+$/.test(this.tokens.peek())) return Number(this.tokens.next()); 526 if (/^true|false$/.test(this.tokens.peek())) return this.tokens.next() === 'true'; 527 if (/^"[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*'$/.test(this.tokens.peek())) { 528 let str = this.tokens.next(); 529 return str.substring(1, str.length - 1).replaceAll(/\\(.)/g, "$1"); 530 } 531 return this.callback(this.tokens.next()); 532 } 533} 534 535function evaluateExpression(expression, callback) { 536 return new Parser(expression, callback).parse(); 537} 538 539function createTable(obj) { 540 const keys = Object.keys(obj); 541 const values = Object.values(obj); 542 543 // Calculate the width of the widest key and value 544 let maxWidthKey = Math.max(...keys.map(key => key.length)); 545 let maxWidthValue = Math.max(...values.map(value => String(value).length)); 546 547 let table = ""; 548 549 // Create the table header 550 table += "â" + "â".repeat(maxWidthKey) + "âââ³â" + "â".repeat(maxWidthValue) + "ââ\n"; 551 table += "â " + "Key".padEnd(maxWidthKey) + " â " + "Value".padEnd(maxWidthValue) + " â\n"; 552 table += "â£" + "â".repeat(maxWidthKey) + "ââââ" + "â".repeat(maxWidthValue) + "ââ«\n"; 553 554 // Iterate over keys and values to create table rows 555 for (let i = 0; i < keys.length; i++) { 556 table += "â " + keys[i].padEnd(maxWidthKey) + " â " + String(values[i]).padEnd(maxWidthValue) + " â\n"; 557 } 558 559 // Create the table footer 560 table += "â" + "â".repeat(maxWidthKey) + "âââ»â" + "â".repeat(maxWidthValue) + "ââ\n"; 561 562 return table; 563} 564 565function setupClickPulse() { 566 const selector = getComputedStyle(document.body).getPropertyValue("--pulse-on-click"); 567 for (const item of document.querySelectorAll(selector)) { 568 item.classList.add("pulse-container") 569 item.addEventListener('click', function (e) { 570 // Create the pulse element 571 const pulse = document.createElement('div'); 572 pulse.classList.add('pulse'); 573 this.appendChild(pulse); 574 575 // Position the pulse element 576 const rect = this.getBoundingClientRect(); 577 const x = e.clientX - rect.left; 578 const y = e.clientY - rect.top; 579 pulse.style.left = `${x}px`; 580 pulse.style.top = `${y}px`; 581 582 // Clean up pulse element after animation 583 pulse.addEventListener('animationend', () => { 584 pulse.remove(); 585 }); 586 }); 587 } 588}
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.