1// Ctrl+<letter> sponsor slots. One table, one handler (Typer.openShortcut): 2// add or retire a slot here, never in the keydown chain. Chrome, Edge and 3// Firefox keep Ctrl+T, Ctrl+N and Ctrl+W for themselves, so those letters can 4// never reach the page - DocuTyper lives on Ctrl+D for that reason. 5// "tab: true" opens an ordinary tab; the rest open the classic 1200x800 window. 6// Ctrl+C / Cmd+C is reserved for real copying (see addText) - never put a slot on C. 7var SHORTCUTS = { 8 A: { url: "https://angel.co/l/2xHh3M", title: "Find Start Up Jobs Near You" }, 9 B: { url: "https://belkin.evyy.net/ORAXJK", title: "Belkin: Tech Made Easy" }, 10 D: { url: "https://docutyper.com/?utm_source=ht-key", title: "DocuTyper", tab: true }, 11 E: { url: "https://visualsitesearch.com", title: "vss" }, 12 // find-dental-plans.com does not answer on https (checked 2026-09-04). 13 F: { url: "http://find-dental-plans.com?t202id=53208", title: "find-dental-plans" }, 14 G: { url: "https://robinhood.com/creditcard/?referral_code=e4748b98", title: "Enlisted" }, 15 J: { 16 url: "https://202.redirexit.com/tracking202/redirect/rtr.php?t202id=13559&t202kw=ogx1-key", 17 title: "Opera GX: The Browser For Gamers", 18 }, 19 L: { url: "https://lovable.dev/invite/2796cac3-b3ca-4dc0-bcc8-5d1b0f07ff0b", title: "Lovable" }, 20 // O for "market Open". The browser's Ctrl+O file dialog is suppressed by the 21 // blanket preventDefault at the end of the keydown handler, same as Ctrl+S/P/D. 22 O: { url: "https://bellhour.com/?utm_source=ht-key", title: "BellHour", tab: true }, 23 P: { 24 url: "https://202.redirexit.com/tracking202/redirect/dl.php?t202id=63448&t202kw=wowd1-key", 25 title: "World Of Warships", 26 }, 27 R: { url: "https://tech.ck.page", title: "Develop a Career in Tech!" }, 28 S: { url: "https://stargateasi.com/?utm_source=ht-key", title: "Stargate ASI", tab: true }, 29}; 30 31var Typer = { 32 text: null, 33 accessCountimer: null, 34 index: 0, // current cursor position 35 speed: 3, // speed of the Typer 36 file: "code.txt", //file, must be setted 37 accessCount: 0, //times alt is pressed for Access Granted 38 deniedCount: 0, //times caps is pressed for Access Denied 39 filesList: null, // loaded files list 40 databaseData: null, // loaded database data 41 init: function () { 42 // inizialize Hacker Typer 43 accessCountimer = setInterval(function () { 44 Typer.updLstChr(); 45 }, 500); // inizialize timer for blinking cursor 46 47 // Embed the code directly for local testing 48 Typer.text = ` 49 50struct group_info init_groups = { .usage = ATOMIC_INIT(2) }; 51 52struct group_info *groups_alloc(int gidsetsize){ 53 54 struct group_info *group_info; 55 56 int nblocks; 57 58 int i; 59 60 61 62 nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK; 63 64 /* Make sure we always allocate at least one indirect block pointer */ 65 66 nblocks = nblocks ? : 1; 67 68 group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER); 69 70 if (!group_info) 71 72 return NULL; 73 74 group_info->ngroups = gidsetsize; 75 76 group_info->nblocks = nblocks; 77 78 atomic_set(&group_info->usage, 1); 79 80 81 82 if (gidsetsize <= NGROUPS_SMALL) 83 84 group_info->blocks[0] = group_info->small_block; 85 86 else { 87 88 for (i = 0; i < nblocks; i++) { 89 90 gid_t *b; 91 92 b = (void *)__get_free_page(GFP_USER); 93 94 if (!b) 95 96 goto out_undo_partial_alloc; 97 98 group_info->blocks[i] = b; 99 100 } 101 102 } 103 104 return group_info; 105 106 107 108out_undo_partial_alloc: 109
110 while (--i >= 0) { 111 112 free_page((unsigned long)group_info->blocks[i]); 113 114 } 115 116 kfree(group_info); 117 118return NULL; 119 120} 121 122 123 124EXPORT_SYMBOL(groups_alloc); 125 126 127 128void groups_free(struct group_info *group_info) 129 130{ 131 132 if (group_info->blocks[0] != group_info->small_block) { 133 134 int i; 135 136 for (i = 0; i < group_info->nblocks; i++) 137 138 free_page((unsigned long)group_info->blocks[i]); 139 140 } 141 142 kfree(group_info); 143 144} 145 146 147 148EXPORT_SYMBOL(groups_free); 149 150 151 152/* export the group_info to a user-space array */ 153 154static int groups_to_user(gid_t __user *grouplist, 155 156 const struct group_info *group_info) 157 158{ 159 160 int i; 161 162 unsigned int count = group_info->ngroups; 163 164 165 166 for (i = 0; i < group_info->nblocks; i++) { 167 168 unsigned int cp_count = min(NGROUPS_PER_BLOCK, count); 169 170 unsigned int len = cp_count * sizeof(*grouplist); 171 172 173 174 if (copy_to_user(grouplist, group_info->blocks[i], len)) 175 176 return -EFAULT; 177 178 179 180 grouplist += NGROUPS_PER_BLOCK; 181 182 count -= cp_count; 183 184 } 185 186 return 0; 187 188} 189 190 191 192/* fill a group_info from a user-space array - it must be allocated already */ 193 194static int groups_from_user(struct group_info *group_info, 195 196 gid_t __user *grouplist) 197 198{ 199 200 int i; 201 202 unsigned int count = group_info->ngroups; 203 204 205 206 for (i = 0; i < group_info->nblocks; i++) { 207 208 unsigned int cp_count = min(NGROUPS_PER_BLOCK, count); 209 210 unsigned int len = cp_count * sizeof(*grouplist); 211 212 213 214 if (copy_from_user(group_info->blocks[i], grouplist, len)) 215 216 return -EFAULT; 217 218 219 220 grouplist += NGROUPS_PER_BLOCK; 221 222 count -= cp_count; 223 224 } 225 226 return 0; 227 228} 229 230 231 232/* a simple Shell sort */ 233 234static void groups_sort(struct group_info *group_info) 235 236{ 237 238 int base, max, stride; 239 240 int gidsetsize = group_info->ngroups; 241 242 243 244 for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1) 245 246 ; /* nothing */ 247 248 stride /= 3; 249 250 251 252 while (stride) { 253 254 max = gidsetsize - stride; 255 256 for (base = 0; base < max; base++) { 257 258 int left = base; 259 260 int right = left + stride; 261 262 gid_t tmp = GROUP_AT(group_info, right); 263 264 265 266 while (left >= 0 && GROUP_AT(group_info, left) > tmp) { 267 268 GROUP_AT(group_info, right) = 269 270 GROUP_AT(group_info, left); 271 272 right = left; 273 274 left -= stride; 275 276 } 277 278 GROUP_AT(group_info, right) = tmp; 279 280 } 281 282 stride /= 3; 283 284 } 285 286} 287 288 289 290/* a simple bsearch */ 291 292int groups_search(const struct group_info *group_info, gid_t grp) 293 294{ 295 296 unsigned int left, right; 297 298 299 300 if (!group_info) 301 302 return 0; 303 304 305 306 left = 0; 307 308 right = group_info->ngroups; 309 310 while (left < right) { 311 312 unsigned int mid = (left+right)/2; 313 314 if (grp > GROUP_AT(group_info, mid)) 315 316 left = mid + 1; 317 318 else if (grp < GROUP_AT(group_info, mid)) 319 320 right = mid; 321 322 else 323 324 return 1; 325 326 } 327 328 return 0; 329 330} 331 332 333 334/** 335 336 * set_groups - Change a group subscription in a set of credentials 337 338 * @new: The newly prepared set of credentials to alter 339 340 * @group_info: The group list to install 341 342 * 343 344 * Validate a group subscription and, if valid, insert it into a set 345 346 * of credentials. 347 348 */ 349 350int set_groups(struct cred *new, struct group_info *group_info) 351 352{ 353 354 put_group_info(new->group_info); 355 356 groups_sort(group_info); 357 358 get_group_info(group_info); 359 360 new->group_info = group_info; 361 362 return 0; 363 364} 365 366 367 368EXPORT_SYMBOL(set_groups); 369 370 371 372/** 373 374 * set_current_groups - Change current's group subscription 375 376 * @group_info: The group list to impose 377 378 * 379 380 * Validate a group subscription and, if valid, impose it upon current's task 381 382 * security record. 383 384 */ 385 386int set_current_groups(struct group_info *group_info) 387 388{ 389 390 struct cred *new; 391 392 int ret; 393 394 395 396 new = prepare_creds(); 397 398 if (!new) 399 400 return -ENOMEM; 401 402 403 404 ret = set_groups(new, group_info); 405 406 if (ret < 0) { 407 408 abort_creds(new); 409 410 return ret; 411 412 } 413 414 415 416 return commit_creds(new); 417 418} 419 420 421 422EXPORT_SYMBOL(set_current_groups); 423 424 425 426SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist) 427 428{ 429 430 const struct cred *cred = current_cred(); 431 432 int i; 433 434 435 436 if (gidsetsize < 0) 437 438 return -EINVAL; 439 440 441 442 /* no need to grab task_lock here; it cannot change */ 443 444 i = cred->group_info->ngroups; 445 446 if (gidsetsize) { 447 448 if (i > gidsetsize) { 449 450 i = -EINVAL; 451 452 goto out; 453 454 } 455 456 if (groups_to_user(grouplist, cred->group_info)) { 457 458 i = -EFAULT; 459 460 goto out; 461 462 } 463 464 } 465 466out: 467 468 return i; 469 470} 471 472 473 474/* 475 476 * SMP: Our groups are copy-on-write. We can set them safely 477
478 * without another task interfering. 479 480 */ 481 482 483 484SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist) 485 486{ 487 488 struct group_info *group_info; 489 490 int retval; 491 492 493 494 if (!nsown_capable(CAP_SETGID)) 495 496 return -EPERM; 497 498 if ((unsigned)gidsetsize > NGROUPS_MAX) 499 500 return -EINVAL; 501 502 503 504 group_info = groups_alloc(gidsetsize); 505 506 if (!group_info) 507 508 return -ENOMEM; 509 510 retval = groups_from_user(group_info, grouplist); 511 512 if (retval) { 513 514 put_group_info(group_info); 515 516 return retval; 517 518 } 519 520 521 522 retval = set_current_groups(group_info); 523 524 put_group_info(group_info); 525 526 527 528 return retval; 529 530} 531 532 533 534/* 535 536 * Check whether we're fsgid/egid or in the supplemental group.. 537 538 */ 539 540int in_group_p(gid_t grp) 541 542{ 543 544 const struct cred *cred = current_cred(); 545 546 int retval = 1; 547 548 549 550 if (grp != cred->fsgid) 551 552 retval = groups_search(cred->group_info, grp); 553 554 return retval; 555 556} 557 558 559 560EXPORT_SYMBOL(in_group_p); 561 562 563 564int in_egroup_p(gid_t grp) 565 566{ 567 568 const struct cred *cred = current_cred(); 569 570 int retval = 1; 571 572 573 574 if (grp != cred->egid) 575 576 retval = groups_search(cred->group_info, grp); 577 578 return retval; 579 580}`; 581 582 // Also try to fetch code.txt for production use 583 fetch(Typer.file) 584 .then((response) => response.text()) 585 .then((data) => { 586 if (data && data.length > 100) { 587 Typer.text = data; 588 } 589 }) 590 .catch(() => { 591 // Use embedded fallback 592 }); 593 594 // Load files list 595 fetch("files.txt") 596 .then((response) => response.text()) 597 .then((data) => { 598 Typer.filesList = data.split('\n').filter(line => line.trim()); 599 }) 600 .catch(() => { 601 // Fallback files list 602 Typer.filesList = [ 603 "/etc/passwd", "/etc/shadow", "/home/admin/.ssh/id_rsa", 604 "/var/log/auth.log", "/root/.bash_history", "/etc/sudoers", 605 "/var/www/html/admin.php", "/tmp/exploit.sh", "/usr/bin/sudo", 606 "/home/user/documents/passwords.txt" 607 ]; 608 }); 609 610 // Load database data 611 fetch("database.txt") 612 .then((response) => response.text()) 613 .then((data) => { 614 Typer.databaseData = data.split('\n').filter(line => line.trim()); 615 }) 616 .catch(() => { 617 // Fallback database data 618 Typer.databaseData = [ 619 "admin:$2b$12$xyz789abc123:[email protected]:1", 620 "root:$2b$12$def456ghi789:[email protected]:0", 621 "jsmith:$2b$12$mno012pqr345:[email protected]:1001" 622 ]; 623 }); 624 }, 625 626 content: function () { 627 return document.querySelector("#console").innerHTML; // get console content 628 }, 629 630 write: function (str) { 631 // append to console content 632 document.querySelector("#console").innerHTML = 633 document.querySelector("#console").innerHTML + str; 634 return false; 635 }, 636 637 makeAccess: function () { 638 //create Access Granted popUp 639 Typer.hidepop(); // hide all popups 640 Typer.accessCount = 0; //reset count 641 document.querySelector("#gran").style.display = "block"; 642 return false; 643 }, 644 645 makeDenied: function () { 646 //create Access Denied popUp 647 Typer.hidepop(); // hide all popups 648 Typer.deniedCount = 0; //reset count 649 document.querySelector("#deni").style.display = "block"; 650 return false; 651 }, 652 653 hidepop: function () { 654 // hide pop ups, and stop whatever the last effect was still doing 655 Typer.clearFx(); 656 document.querySelector("#deni").style.display = "none"; 657 document.querySelector("#gran").style.display = "none"; 658 document.querySelector("#breach").style.display = "none"; 659 document.querySelector("#camera").style.display = "none"; 660 document.querySelector("#gps").style.display = "none"; 661 document.querySelector("#filesystem").style.display = "none"; 662 document.querySelector("#database").style.display = "none"; 663 }, 664 665 trackShortcut: function (label, url, via) { 666 // One event for every sponsor slot, keyboard or footer, so GTM can count 667 // clicks per slot. Fails silently: measurement never blocks the click. 668 try { 669 window.dataLayer = window.dataLayer || []; 670 window.dataLayer.push({ event: "sponsor_click", slot: label, slot_url: url, via: via }); 671 if (typeof gtag !== "undefined") { 672 gtag("event", "sponsor_click", { slot: label, via: via }); 673 } 674 } catch (e) {} 675 }, 676 677 openShortcut: function (letter, via) { 678 var slot = SHORTCUTS[letter]; 679 if (!slot) return false;
680 Typer.trackShortcut("Ctrl+" + letter + " " + slot.title, slot.url, via); 681 if (slot.tab) { 682 window.open(slot.url, "_blank"); 683 } else { 684 window.open(slot.url, slot.title, "height=800,width=1200,menubar=1,status=1,scrollbars=1"); 685 } 686 return true; 687 }, 688 689 trackHackerEffect: function (action) { 690 // Track hacker effects usage in Google Analytics 691 try { 692 // Google Tag Manager / GA4 tracking 693 if (typeof gtag !== 'undefined') { 694 gtag('event', action, { 695 'event_category': 'Hacker_Effects' 696 }); 697 } 698 } catch (e) { 699 // Fail silently if analytics not loaded 700 console.log('Analytics tracking failed:', e); 701 } 702 }, 703 704 // Effect timers. Every timeout and interval an effect starts is registered 705 // here and cleared by hidepop (Escape, or the next effect), so a re-triggered 706 // effect never stacks on its previous run and nothing keeps ticking behind a 707 // hidden popup. Before this, each Shift+3 press added another GPS loop that 708 // ran for the life of the page. 709 fxTimers: [], 710 fxTimeout: function (fn, ms) { var id = setTimeout(fn, ms); Typer.fxTimers.push(id); return id; }, 711 fxInterval: function (fn, ms) { var id = setInterval(fn, ms); Typer.fxTimers.push(id); return id; }, 712 clearFx: function () { 713 Typer.fxTimers.forEach(function (id) { clearTimeout(id); clearInterval(id); }); 714 Typer.fxTimers = []; 715 }, 716 717 // Phase strings the effects write while they run. Locale pages carry them 718 // as effects.phases in their i18n table; the static Portuguese page and the 719 // English root use these. {n} is the count. 720 fxPhases: { 721 en: { 722 threatLevel: "Threat level:", 723 critical: "CRITICAL", 724 protocols: "â ï¸ SECURITY PROTOCOLS ACTIVATED", 725 cameraLabel: "ð· Camera:", 726 initializing: "INITIALIZING", 727 active: "ACTIVE", 728 acquiring: "ð± Acquiring satellite lock", 729 locked: "ð± Satellite lock acquired", 730 tracking: "ð°ï¸ TRACKING ACTIVE", 731 found: "Found:", 732 scanComplete: "ð Scan complete. {n} files indexed.", 733 connected: "Connected to MySQL server", 734 executing: "Executing query", 735 streaming: "Streaming results", 736 completed: "â Query completed. {n} rows returned." 737 }, 738 pt: { 739 threatLevel: "NÃvel de ameaça:", 740 critical: "CRÃTICO", 741 protocols: "â ï¸ PROTOCOLOS DE SEGURANÃA ATIVADOS", 742 cameraLabel: "ð· Câmera:", 743 initializing: "INICIALIZANDO", 744 active: "ATIVA", 745 acquiring: "ð± Obtendo sinal de satélite", 746 locked: "ð± Sinal de satélite obtido", 747 tracking: "ð°ï¸ RASTREAMENTO ATIVO", 748 found: "Encontrado:", 749 scanComplete: "ð Varredura concluÃda. {n} arquivos indexados.", 750 connected: "Conectado ao servidor MySQL", 751 executing: "Executando consulta", 752 streaming: "Transmitindo resultados", 753 completed: "â Consulta concluÃda. {n} linhas retornadas." 754 } 755 }, 756 fxText: function (key, n) { 757 var lang = (document.documentElement.lang || "en").toLowerCase().split("-")[0]; 758 var table = null; 759 try { 760 if (typeof translations !== "undefined" && translations[lang] && translations[lang].effects) { 761 table = translations[lang].effects.phases; 762 } 763 } catch (e) {} 764 var s = (table && table[key]) || (Typer.fxPhases[lang] || {})[key] || Typer.fxPhases.en[key] || ""; 765 return s.replace("{n}", n); 766 }, 767 // Status lines are rewritten as an effect runs; remember what the page 768 // shipped so a second run starts from the beginning, not from the end. 769 fxReset: function (id) { 770 var el = document.getElementById(id); 771 if (!el) return null; 772 if (el.getAttribute("data-initial") === null) el.setAttribute("data-initial", el.innerHTML); 773 el.innerHTML = el.getAttribute("data-initial"); 774 return el; 775 }, 776 fxScroll: function (id) { var el = document.getElementById(id); if (el) el.scrollTop = el.scrollHeight; }, 777 fxCoord: function (value, positive, negative) { 778 return Math.abs(value).toFixed(4) + "° " + (value >= 0 ? positive : negative); 779 }, 780 781 makeSystemBreach: function () { 782 Typer.hidepop(); 783 document.querySelector("#breach").style.display = "block"; 784 Typer.trackHackerEffect("system_breach"); 785 var status = Typer.fxReset("breach-status"); 786 if (!status) return false;
787 Typer.fxTimeout(function () { 788 status.innerHTML = Typer.fxText("threatLevel") + " <span style='color:#FF6666'>" + Typer.fxText("critical") + "</span>"; 789 }, 3000); 790 Typer.fxTimeout(function () { status.textContent = Typer.fxText("protocols"); }, 5000); 791 return false; 792 }, 793 794 makeCameraAccess: function () { 795 Typer.hidepop(); 796 document.querySelector("#camera").style.display = "block"; 797 Typer.trackHackerEffect("camera_access"); 798 var status = Typer.fxReset("camera-status"); 799 var wave = document.getElementById("soundwave-display"); 800 if (wave) wave.style.display = "none"; 801 if (!status) return false; 802 Typer.fxTimeout(function () { 803 status.innerHTML = Typer.fxText("cameraLabel") + " <span style='color:#4a90e2'>" + Typer.fxText("initializing") + "</span>"; 804 }, 2000); 805 Typer.fxTimeout(function () { 806 status.innerHTML = Typer.fxText("cameraLabel") + " <span style='color:#00FF00'>" + Typer.fxText("active") + "</span>"; 807 if (wave) wave.style.display = "flex"; 808 }, 4000); 809 return false; 810 }, 811 812 makeGpsTracking: function () { 813 Typer.hidepop(); 814 document.querySelector("#gps").style.display = "block"; 815 Typer.trackHackerEffect("gps_tracking"); 816 817 var cities = [ 818 { lat: 37.7749, lng: -122.4194 }, { lat: 40.7128, lng: -74.0060 }, { lat: 51.5074, lng: -0.1278 }, 819 { lat: 48.8566, lng: 2.3522 }, { lat: 35.6762, lng: 139.6503 }, { lat: -33.8688, lng: 151.2093 }, 820 { lat: 55.7558, lng: 37.6173 }, { lat: 39.9042, lng: 116.4074 }, { lat: 52.5200, lng: 13.4050 }, 821 { lat: 41.9028, lng: 12.4964 }, { lat: 19.4326, lng: -99.1332 }, { lat: -23.5505, lng: -46.6333 }, 822 { lat: 28.6139, lng: 77.2090 }, { lat: 1.3521, lng: 103.8198 }, { lat: 25.2048, lng: 55.2708 } 823 ]; 824 var city = cities[Math.floor(Math.random() * cities.length)]; 825 var lat = city.lat + (Math.random() - 0.5) * 0.02; 826 var lng = city.lng + (Math.random() - 0.5) * 0.02; 827 var latEl = document.getElementById("lat"), lngEl = document.getElementById("lng"); 828 var accEl = document.getElementById("accuracy"); 829 var status = Typer.fxReset("gps-status"); 830 // Hemisphere follows the sign: Sydney reads S and E, not a negative N. 831 function paint(spread) { 832 lat += (Math.random() - 0.5) * spread; 833 lng += (Math.random() - 0.5) * spread; 834 if (latEl) latEl.textContent = Typer.fxCoord(lat, "N", "S"); 835 if (lngEl) lngEl.textContent = Typer.fxCoord(lng, "E", "W"); 836 if (accEl) accEl.textContent = "±" + (Math.floor(Math.random() * 6) + 2) + "m"; 837 } 838 paint(0); 839 var updates = 0; 840 var lock = Typer.fxInterval(function () { 841 paint(0.002); 842 updates++; 843 if (updates === 1 && status) status.textContent = Typer.fxText("locked"); 844 if (updates >= 8) { 845 clearInterval(lock); 846 if (status) status.innerHTML = "<span style='color:#00FF00'>" + Typer.fxText("tracking") + "</span>"; 847 var drift = function () { 848 paint(0.001); 849 Typer.fxTimeout(drift, Math.random() * 5000 + 5000); 850 }; 851 Typer.fxTimeout(drift, 5000); 852 } 853 }, 1000); 854 return false; 855 }, 856 857 makeFileSystem: function () { 858 Typer.hidepop(); 859 var box = document.querySelector("#filesystem"); 860 box.style.display = "block"; 861 Typer.trackHackerEffect("file_scanner"); 862 box.classList.add("scan-effect"); 863 var list = Typer.fxReset("file-list"); 864 if (!list) return false; 865 box.scrollTop = 0; 866 867 var files; 868 if (Typer.filesList && Typer.filesList.length > 0) { 869 var want = Math.min(Math.floor(Math.random() * 6) + 10, Typer.filesList.length); 870 var pool = Typer.filesList.slice(); 871 files = []; 872 while (files.length < want) files.push(pool.splice(Math.floor(Math.random() * pool.length), 1)[0]); 873 } else { 874 files = [ 875 "/etc/passwd", "/etc/shadow", "/home/admin/.ssh/id_rsa", 876 "/var/log/auth.log", "/root/.bash_history", "/etc/sudoers", 877 "/var/www/html/admin.php", "/tmp/exploit.sh", "/usr/bin/sudo", 878 "/home/user/documents/passwords.txt" 879 ]; 880 } 881 var i = 0; 882 var scan = Typer.fxInterval(function () { 883 var line = document.createElement("p"); 884 if (i < files.length) { 885 line.style.color = "#FFFF00"; 886 line.textContent = Typer.fxText("found") + " " + files[i++]; 887 } else { 888 clearInterval(scan); 889 line.style.color = "#00FF00"; 890 line.textContent = Typer.fxText("scanComplete", files.length); 891 box.classList.remove("scan-effect"); 892 } 893 list.appendChild(line); 894 Typer.fxScroll("filesystem"); 895 }, 800); 896 return false;
897 }, 898 899 makeDatabaseQuery: function () { 900 Typer.hidepop(); 901 var box = document.querySelector("#database"); 902 box.style.display = "block"; 903 Typer.trackHackerEffect("database_query"); 904 var status = Typer.fxReset("db-status"); 905 var results = document.getElementById("db-results"); 906 if (!status || !results) return false; 907 status.style.display = "block"; 908 results.style.display = "none"; 909 results.innerHTML = ""; 910 box.scrollTop = 0; 911 912 Typer.fxTimeout(function () { status.textContent = Typer.fxText("connected"); }, 2000); 913 Typer.fxTimeout(function () { status.innerHTML = Typer.fxText("executing") + "<span class='loading-dots'></span>"; }, 3500); 914 Typer.fxTimeout(function () { 915 status.innerHTML = Typer.fxText("streaming") + "<span class='loading-dots'></span>"; 916 results.style.display = "block"; 917 var max = Math.floor(Math.random() * 8) + 5; 918 var n = 0; 919 var add = function (text, color) { 920 var line = document.createElement("p"); 921 line.style.color = color; 922 line.textContent = text; 923 results.appendChild(line); 924 Typer.fxScroll("database"); 925 }; 926 add("mysql> SELECT username,email,uid FROM users WHERE active=1;", "#00FFFF"); 927 var stream = Typer.fxInterval(function () { 928 if (n < max) { 929 var row = null; 930 if (Typer.databaseData && Typer.databaseData.length > 0) { 931 var entry = Typer.databaseData[Math.floor(Math.random() * Typer.databaseData.length)].split(":"); 932 if (entry.length >= 4) row = entry[0] + " | " + entry[2] + " | " + entry[3]; 933 } 934 if (!row) { 935 var users = ["admin", "root", "jsmith", "sarah_connor", "neo_anderson"]; 936 var u = users[Math.floor(Math.random() * users.length)]; 937 row = u + " | " + u + "@system.com | " + (1000 + n); 938 } 939 add(row, "#FFFFFF"); 940 n++; 941 } else { 942 clearInterval(stream); 943 status.style.display = "none"; 944 add(Typer.fxText("completed", n), "#00FF00"); 945 } 946 }, 600); 947 }, 5000); 948 return false; 949 }, 950 951 addText: function (key) { 952 //Main function to add the code 953 if ((key.ctrlKey || key.metaKey) && key.keyCode == 67) { 954 // Ctrl+C / Cmd+C: people copy the code for real. Let the browser handle it - 955 // no typing, no preventDefault. (Slot C was "Buy Bitcoin Here" until 2026-09-06.) 956 try { 957 window.dataLayer = window.dataLayer || []; 958 window.dataLayer.push({ event: "copy_key" }); 959 } catch (e) {} 960 return; 961 } 962 if (key.keyCode == 18) { 963 // key 18 = alt key 964 Typer.accessCount++; //increase counter 965 if (Typer.accessCount >= 3) { 966 // if it's presed 3 times 967 Typer.makeAccess(); // make access popup 968 } 969 } else if (key.keyCode == 20) { 970 // key 20 = caps lock 971 Typer.deniedCount++; // increase counter 972 if (Typer.deniedCount >= 3) { 973 // if it's pressed 3 times 974 Typer.makeDenied(); // make denied popup 975 } 976 } else if (key.keyCode == 27) { 977 // key 27 = esc key 978 Typer.hidepop(); // hide all popups 979 } else if (key.keyCode == 49 && key.shiftKey) { 980 // key 1 + shift = ! key (system breach) 981 Typer.makeSystemBreach(); 982 } else if (key.keyCode == 50 && key.shiftKey) { 983 // key 2 + shift = @ key (camera access) 984 Typer.makeCameraAccess(); 985 } else if (key.keyCode == 51 && key.shiftKey) { 986 // key 3 + shift = # key (GPS tracking) 987 Typer.makeGpsTracking(); 988 } else if (key.keyCode == 52 && key.shiftKey) { 989 // key 4 + shift = $ key (file system) 990 Typer.makeFileSystem(); 991 } else if (key.keyCode == 53 && key.shiftKey) { 992 // key 5 + shift = % key (database query) 993 Typer.makeDatabaseQuery(); 994 } else if ( 995 (key.ctrlKey == true || key.metaKey == true) && 996 key.keyCode == "72" 997 ) { 998 // key 72 = H key show help 999 document.getElementById("settings").style.display = "block"; 1000 document.querySelector(".subscribe-section").style.display = "none"; 1001 } else if ( 1002 (key.ctrlKey == true || key.metaKey == true) && 1003 key.keyCode == "77" 1004 ) { 1005 // key 77 = M key show menu 1006 document.querySelector(".subscribe-section").style.display = "block"; 1007 } else if (key.ctrlKey == true && SHORTCUTS[String.fromCharCode(key.keyCode)]) { 1008 // Ctrl + a letter from the SHORTCUTS table opens that sponsor slot. 1009 Typer.openShortcut(String.fromCharCode(key.keyCode), "key"); 1010 } else if (Typer.text) { 1011 // otherway if text is loaded 1012 var cont = Typer.content(); // get the console content 1013 if (cont.substring(cont.length - 1, cont.length) == "|") 1014 // if the last char is the blinking cursor 1015 document.querySelector("#console").innerHTML = document 1016 .querySelector("#console") 1017 .innerHTML.substring(0, cont.length - 1); // remove it before adding the text 1018 if (key.keyCode != 8) { 1019 // if key is not backspace 1020 Typer.index += Typer.speed; // add to the index the speed 1021 } else { 1022 if (Typer.index > 0) 1023 // else if index is not less than 0 1024 Typer.index -= Typer.speed; // remove speed for deleting text 1025 } 1026 // var text = $('<div/>').text(Typer.text.substring(0, Typer.index)).html(); // parse the text for stripping html enities 1027 var text = Typer.text.substring(0, Typer.index); 1028 var rtn = new RegExp("\n", "g"); // newline regex 1029 var rts = new RegExp("\\s", "g"); // whitespace regex 1030 var rtt = new RegExp("\\t", "g"); // tab regex 1031 1032 document.querySelector("#console").innerHTML = text 1033 .replace(rtn, "<br/>") 1034 .replace(rtt, " ") 1035 .replace(rts, " "); 1036 // replace newline chars with br, tabs with 4 space and blanks with an html blank 1037 // Scroll to keep content visible above the footer 1038 let footerHeight = 40; 1039 if (window.innerWidth <= 480) { 1040 footerHeight = 60; 1041 } else if (window.innerWidth <= 768) { 1042 footerHeight = 50; 1043 } 1044 1045 const windowHeight = window.innerHeight; 1046 const console = document.querySelector("#console"); 1047 const consoleBottom = console.getBoundingClientRect().bottom; 1048 1049 if (consoleBottom > (windowHeight - footerHeight - 20)) { 1050 window.scrollBy(0, 50); 1051 } 1052 } 1053 if (key.preventDefault && key.keyCode != 122) { 1054 // prevent F11(fullscreen) from being blocked 1055 key.preventDefault(); 1056 } 1057 if (key.keyCode != 122) { 1058 // otherway prevent keys default behavior 1059 key.returnValue = false;
1060 } 1061 }, 1062 1063 updLstChr: function () { 1064 // blinking cursor 1065 var cont = this.content(); // get console 1066 if (cont.substring(cont.length - 1, cont.length) == "|") 1067 // if last char is the cursor 1068 document.querySelector("#console").innerHTML = document 1069 .querySelector("#console") 1070 .innerHTML.substring(0, cont.length - 1); 1071 else this.write("|"); // else write it 1072 }, 1073}; 1074 1075Typer.init(); 1076 1077document.addEventListener("touchstart", (e) => { 1078 if (e.target.id === "subscribe-btn") return; 1079 document.getElementById("settings").style.display = "none"; 1080 if (e.touches && e.touches.length >= 3) { 1081 // Three fingers down is the phone's Alt-Alt-Alt. 1082 Typer.makeAccess(); 1083 return; 1084 } 1085 Typer.addText(e); //Capture the tap event for mobile and call the addText, this is executed on page load 1086}); 1087 1088document.addEventListener("keydown", (e) => { 1089 document.getElementById("settings").style.display = "none"; 1090 document.querySelector(".subscribe-section").style.display = "none"; 1091 Typer.addText(e); //Capture the keydown event and call the addText, this is executed on page load 1092}); 1093 1094document.querySelector("#showhelp").addEventListener("click", (e) => { 1095 document.getElementById("settings").style.display = "block"; 1096 var element = document.getElementById("subscribe-section"); 1097 if (element) { 1098 element.style.display = "none"; 1099 } 1100}); 1101 1102document.querySelector("#showmenu").addEventListener("click", (e) => { 1103 document.getElementById("settings").style.display = "none"; 1104 var element = document.getElementById("subscribe-section"); 1105 if (element) { 1106 element.style.display = "block"; 1107 } 1108}); 1109 1110document.querySelector(".settings-btn").addEventListener("click", (e) => { 1111 document.getElementById("settings").style.display = "none"; 1112}); 1113 1114// Footer Stargate link A/B. The label is the only variable; assignment is per 1115// visitor, sticky in localStorage, 50/50. The variant rides on utm_campaign, 1116// which stargateasi.com already records as an attribution facet, so "runs 1117// started by campaign tag" on its dashboard is the readout. utm_source stays 1118// "ht" so the existing series is unbroken. Ctrl+S (ht-key) is not in the test. 1119// Locale pages take the label from their i18n table; root and br from the 1120// link's data-variant-* attributes. 1121var FOOTER_VARIANTS = { a: "footer-stargate", b: "footer-survive" }; 1122(function () { 1123 var link = document.getElementById("stargate-link"); 1124 var label = document.getElementById("stargate-label"); 1125 if (!link || !label) return; 1126 var v = null; 1127 try { v = localStorage.getItem("htFooterVariant"); } catch (e) {} 1128 if (v !== "a" && v !== "b") { 1129 v = Math.random() < 0.5 ? "a" : "b"; 1130 try { localStorage.setItem("htFooterVariant", v); } catch (e) {} 1131 } 1132 var lang = document.documentElement.lang; 1133 var t = (typeof translations !== "undefined" && translations[lang] && translations[lang].ui) || null; 1134 var text = v === "b" 1135 ? (t && t.stargateAlt) || link.getAttribute("data-variant-b") 1136 : (t && t.stargate) || link.getAttribute("data-variant-a"); 1137 if (text) label.textContent = text; 1138 label.setAttribute("data-i18n", v === "b" ? "ui.stargateAlt" : "ui.stargate"); 1139 try { 1140 var url = new URL(link.href); 1141 url.searchParams.set("utm_campaign", FOOTER_VARIANTS[v]); 1142 link.href = url.toString(); 1143 } catch (e) {} 1144 try { 1145 window.dataLayer = window.dataLayer || []; 1146 window.dataLayer.push({ event: "footer_variant", footer_variant: FOOTER_VARIANTS[v] }); 1147 } catch (e) {} 1148})(); 1149 1150// Footer sponsor links and the Access Granted promo report through the same 1151// event as the keyboard slots, so one GTM report covers every way in. 1152document.querySelectorAll("#menu a[target='_blank'], #gran .granted-promo a").forEach(function (link) { 1153 link.addEventListener("click", function () { 1154 Typer.trackShortcut(link.textContent.replace(/\s+/g, " ").trim(), link.href, "footer"); 1155 }); 1156}); 1157 1158// Share from the Access Granted popup: the native share sheet where there is 1159// one, otherwise copy a tagged link to this page and say so. No third party. 1160(function () { 1161 var share = document.getElementById("granted-share"); 1162 if (!share) return; 1163 var label = share.textContent; 1164 share.addEventListener("click", function (e) { 1165 e.preventDefault(); 1166 e.stopPropagation(); 1167 var canonical = document.querySelector('link[rel="canonical"]'); 1168 var url = (canonical ? canonical.href : "https://hackertyper.com/") + "?utm_source=share"; 1169 var heading = document.querySelector("#gran h2"); 1170 var text = (heading ? heading.textContent.trim() : "ACCESS GRANTED") + " - hackertyper.com"; 1171 try { 1172 window.dataLayer = window.dataLayer || []; 1173 window.dataLayer.push({ event: "share_click", via: "granted", method: navigator.share ? "native" : "copy" }); 1174 } catch (err) {} 1175 if (navigator.share) { 1176 navigator.share({ title: "Hacker Typer", text: text, url: url }).catch(function () {}); 1177 return; 1178 } 1179 if (navigator.clipboard && navigator.clipboard.writeText) { 1180 navigator.clipboard.writeText(url).then(function () { 1181 share.textContent = share.getAttribute("data-copied") || "Link copied"; 1182 setTimeout(function () { share.textContent = label; }, 2000); 1183 }, function () {}); 1184 } 1185 }); 1186})(); 1187 1188// Mobile footer: a shell prompt line. On narrow touch screens the keyboard 1189// badges mean nothing and the five-cell footer overflows the viewport, so it 1190// becomes [?] [â¡] $ run survive-the-aiâ - two fixed controls and one 1191// sponsor slot shown as a ghost autosuggestion that backspaces and retypes 1192// the next slot every few seconds: the site's own typing effect, not a banner 1193// fade. Slots come from the footer links, so the A/B label, the locale text 1194// and the tracking carry over. Stargate alternates with every other slot 1195// (Stargate, DocuTyper, Stargate, Lovable, ...) and holds longest. Rotation
1196// pauses while a popup is up or the tab is hidden, and never starts under 1197// prefers-reduced-motion. Every fully typed slot pushes sponsor_impression, 1198// so tap rate per slot is taps over the times it was actually on screen. 1199// [fx] opens a command list for the effects that need Shift or Caps Lock on 1200// a keyboard, named from the help panel so locale pages get their own words. 1201// Desktop keeps the badge footer; the bar only exists below PROMPT_BAR.query. 1202// Two breakpoints, not one. The footer is swapped for the bar wherever it no 1203// longer fits - measured at 822px with six slots - while the popup and console 1204// rules below stay phone-only. 1205var PROMPT_BAR = { 1206 query: "(max-width: 830px)", 1207 phoneQuery: "(max-width: 640px)", 1208 holdMs: { first: 8000, rest: 5000 }, 1209 typeMs: 45, 1210 eraseMs: 25, 1211}; 1212(function () { 1213 var footer = document.getElementById("menu"); 1214 if (!footer || !window.matchMedia) return; 1215 var links = Array.prototype.slice.call(footer.querySelectorAll("a[target='_blank']")); 1216 if (!links.length) return; 1217 1218 var slots = links.map(function (a) { 1219 var label = a.textContent.replace(/\s+/g, " ").replace(/Ctrl\+\w\s*/, "").trim(); 1220 var slug = label.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-|-$/g, ""); 1221 var lead = /stargateasi\.com/.test(a.href); 1222 return { label: label, url: a.href, command: (lead ? "run " : "open ") + slug, lead: lead }; 1223 }); 1224 // Play order: the lead slot between every other slot, so it is on screen 1225 // half the time and never more than one slot away. 1226 var lead = slots.filter(function (s) { return s.lead; })[0] || slots[0]; 1227 var others = slots.filter(function (s) { return s !== lead; }); 1228 var sequence = []; 1229 others.forEach(function (s) { sequence.push(lead, s); }); 1230 if (!sequence.length) sequence = [lead]; 1231 1232 var style = document.createElement("style"); 1233 style.textContent = [ 1234 ".prompt-bar { display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;", 1235 " align-items: center; gap: 10px; height: 44px; padding: 0 12px env(safe-area-inset-bottom);", 1236 " background: #000; border-top: 1px solid #00FF00; color: #00FF00;", 1237 " font-family: monospace; font-size: 15px; line-height: 44px; }", 1238 ".prompt-key { background: #00FF00; color: #000; border: 0; padding: 0 3px; margin: 0;", 1239 " font: inherit; line-height: 20px; cursor: pointer; }", 1240 ".prompt-line { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden;", 1241 " background: transparent; color: #00FF00; text-decoration: none; }", 1242 ".prompt-ghost { opacity: 0.65; }", 1243 ".fx-menu { display: none; position: fixed; left: 0; right: 0; bottom: 44px; z-index: 41;", 1244 " padding: 6px 12px 10px; background: #000; border-top: 1px solid #00FF00; color: #00FF00;", 1245 " font-family: monospace; font-size: 15px; line-height: 1.2; }", 1246 ".fx-menu .fx-title { opacity: 0.65; padding: 4px 0 6px; }", 1247 ".fx-menu button { display: block; width: 100%; text-align: left; background: transparent;", 1248 " color: #00FF00; border: 0; padding: 7px 0; margin: 0; font: inherit; cursor: pointer; }", 1249 ".fx-menu button .prompt-key { display: inline-block; min-width: 3.2em; text-align: center; margin-right: 8px; }", 1250 ".prompt-cursor { animation: prompt-blink 1s steps(1) infinite; }", 1251 "@keyframes prompt-blink { 50% { opacity: 0; } }", 1252 "@media " + PROMPT_BAR.query + " { .prompt-bar { display: flex; } #menu { display: none; } }", 1253 "@media " + PROMPT_BAR.phoneQuery + " {", 1254 " .systemBreach { width: auto; left: 12px; right: 12px; margin-left: 0; font-size: 1.3em; padding: 18px; }", 1255 " .cameraAccess, .gpsTracking, .fileSystem, .databaseQuery { width: auto; left: 12px; right: 12px; }", 1256 " .databaseQuery { bottom: 60px; }", 1257 // Clip sideways overflow at the viewport (html), never on body: the base 1258 // rule gives body height:100%, and a non-visible overflow there turns body 1259 // into its own scroller, so window.scrollBy in addText stops following the text. 1260 " html { overflow-x: hidden; }", 1261 " #console { max-width: 100%; overflow-wrap: anywhere; word-break: break-all; }", 1262 "}", 1263 "@media (prefers-reduced-motion: reduce) { .prompt-cursor { animation: none; } }", 1264 ].join("\n"); 1265 document.head.appendChild(style); 1266 1267 var bar = document.createElement("div"); 1268 bar.id = "prompt-bar"; 1269 bar.className = "prompt-bar"; 1270 bar.innerHTML = 1271 '<button type="button" class="prompt-key" id="prompt-help" aria-label="Help">[?]</button>' + 1272 '<button type="button" class="prompt-key" id="prompt-menu" aria-label="Menu">[â¡]</button>' + 1273 '<button type="button" class="prompt-key" id="prompt-fx" aria-label="Effects" aria-expanded="false">[fx]</button>' + 1274 '<a class="prompt-line" id="prompt-line" href="#" rel="noopener">' + 1275 '<span class="prompt-ps1">$</span> <span class="prompt-ghost" id="prompt-ghost"></span>' + 1276 '<span class="prompt-cursor">â</span></a>'; 1277 document.body.appendChild(bar); 1278 1279 var ghost = document.getElementById("prompt-ghost"); 1280 var line = document.getElementById("prompt-line"); 1281 var reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches; 1282 var idx = 0, timer = null, running = false, current = sequence[0]; 1283 1284 function popupOpen() { 1285 return ["gran", "deni", "breach", "camera", "gps", "filesystem", "database", "settings", "subscribe-section", "fx-menu"] 1286 .some(function (id) { var el = document.getElementById(id); return el && el.style.display === "block"; }); 1287 } 1288 function impression(slot) { 1289 try { 1290 window.dataLayer = window.dataLayer || []; 1291 window.dataLayer.push({ event: "sponsor_impression", slot: slot.label, slot_url: slot.url, via: "prompt" }); 1292 } catch (e) {} 1293 } 1294 function typeTo(text, done) { 1295 var i = 0; 1296 ghost.textContent = ""; 1297 (function step() { 1298 if (i < text.length) { ghost.textContent = text.slice(0, ++i); timer = setTimeout(step, PROMPT_BAR.typeMs); } 1299 else done(); 1300 })(); 1301 } 1302 function erase(done) { 1303 (function step() { 1304 var t = ghost.textContent; 1305 if (t.length) { ghost.textContent = t.slice(0, -1); timer = setTimeout(step, PROMPT_BAR.eraseMs); } 1306 else done(); 1307 })(); 1308 } 1309 function schedule(ms) { clearTimeout(timer); timer = setTimeout(next, ms); } 1310 function next() { 1311 if (!window.matchMedia(PROMPT_BAR.query).matches) { running = false; return; } 1312 if (document.hidden || popupOpen()) { schedule(1000); return; } 1313 erase(function () { 1314 idx = (idx + 1) % sequence.length; 1315 current = sequence[idx]; 1316 line.href = current.url; 1317 typeTo(current.command, function () { 1318 impression(current); 1319 schedule(current.lead ? PROMPT_BAR.holdMs.first : PROMPT_BAR.holdMs.rest); 1320 }); 1321 }); 1322 } 1323 function start() { 1324 if (running) return; 1325 running = true; 1326 idx = 0; 1327 current = sequence[0]; 1328 ghost.textContent = current.command; 1329 line.href = current.url; 1330 impression(current); 1331 if (!reduced && sequence.length > 1) schedule(PROMPT_BAR.holdMs.first); 1332 } 1333 var mq = window.matchMedia(PROMPT_BAR.query); 1334 if (mq.matches) start(); 1335 if (mq.addEventListener) mq.addEventListener("change", function (e) { if (e.matches) start(); }); 1336 1337 // The effects menu. Names come from the help panel lines "! (Shift+1) = X" 1338 // (already translated on locale pages) and the two popup headings. 1339 var effects = []; 1340 Array.prototype.forEach.call(document.querySelectorAll("#settings p"), function (p) { 1341 var m = p.textContent.trim().match(/^(\S)\s*\(Shift\+([1-5])\)\s*=\s
1341*(.+)$/); 1342 if (m) effects.push({ key: m[1], name: m[3].trim(), run: ["makeSystemBreach", "makeCameraAccess", "makeGpsTracking", "makeFileSystem", "makeDatabaseQuery"][Number(m[2]) - 1] }); 1343 }); 1344 var granH2 = document.querySelector("#gran h2"), deniH2 = document.querySelector("#deni h2"); 1345 effects.push({ key: "ok", name: granH2 ? granH2.textContent.trim() : "ACCESS GRANTED", run: "makeAccess" }); 1346 effects.push({ key: "x", name: deniH2 ? deniH2.textContent.trim() : "ACCESS DENIED", run: "makeDenied" }); 1347 effects.push({ key: "esc", name: "clear", run: "hidepop" }); 1348 1349 var fxMenu = document.createElement("div"); 1350 fxMenu.id = "fx-menu"; 1351 fxMenu.className = "fx-menu"; 1352 fxMenu.setAttribute("role", "menu"); 1353 fxMenu.innerHTML = '<div class="fx-title">$ fx --list</div>' + effects.map(function (fx, i) { 1354 return '<button type="button" role="menuitem" data-fx="' + i + '"><span class="prompt-key">[' + fx.key + ']</span>' + fx.name.replace(/</g, "<") + "</button>"; 1355 }).join(""); 1356 document.body.appendChild(fxMenu); 1357 var fxKey = document.getElementById("prompt-fx"); 1358 function setFx(open) { 1359 fxMenu.style.display = open ? "block" : "none"; 1360 fxKey.setAttribute("aria-expanded", open ? "true" : "false"); 1361 } 1362 fxKey.addEventListener("click", function () { setFx(fxMenu.style.display !== "block"); }); 1363 fxMenu.addEventListener("click", function (e) { 1364 var b = e.target.closest ? e.target.closest("button[data-fx]") : null; 1365 if (!b) return; 1366 var fx = effects[Number(b.getAttribute("data-fx"))]; 1367 setFx(false); 1368 if (fx && typeof Typer[fx.run] === "function") Typer[fx.run](); 1369 }); 1370 document.addEventListener("click", function (e) { 1371 if (fxMenu.style.display === "block" && !fxMenu.contains(e.target) && e.target !== fxKey) setFx(false); 1372 }); 1373 fxMenu.addEventListener("touchstart", function (e) { e.stopPropagation(); }); 1374 1375 // A tap on the bar is a tap on the bar, not a keystroke for the typer. 1376 bar.addEventListener("touchstart", function (e) { e.stopPropagation(); }); 1377 document.getElementById("prompt-help").addEventListener("click", function () { 1378 var h = document.getElementById("showhelp"); if (h) h.click(); 1379 }); 1380 document.getElementById("prompt-menu").addEventListener("click", function () { 1381 var m = document.getElementById("showmenu"); if (m) m.click(); 1382 }); 1383 line.addEventListener("click", function (e) { 1384 e.preventDefault(); 1385 Typer.trackShortcut(current.label, current.url, "prompt"); 1386 window.open(current.url, "_blank"); 1387 }); 1388})();
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.