Last active
July 29, 2026 21:09
-
-
Save MustCodeAl/6418d9536ce628448614263732e6e2be to your computer and use it in GitHub Desktop.
Install with tamper monkey, adds highlights to common links for developers, to increase your efficiency when googling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Google DevSearch Power Edition — Crawler Engine v15.0 (Custom UI Edition) | |
| // @description Zero-latency crawler with 400+ domains. Now features a built-in UI to add your own custom sites and blacklists! | |
| // @version 15.0 | |
| // @include *://www.google.*/* | |
| // @run-at document-end | |
| // @grant none | |
| // ==/UserScript== | |
| (() => { | |
| 'use strict'; | |
| /********************************************************************** | |
| * 1. CSS INJECTION (Dark Mode, Tags, & UI Settings Panel) | |
| **********************************************************************/ | |
| const injectStyles = () => { | |
| if (document.getElementById('gdev-styles')) return; | |
| const style = document.createElement('style'); | |
| style.id = 'gdev-styles'; | |
| style.innerHTML = ` | |
| /* Existing Tag & Highlight Styles */ | |
| .gdev-tag { | |
| background: var(--gdev-tag-bg, #e5e7eb); | |
| color: var(--gdev-tag-text, #1f2937); | |
| padding: 2px 6px; font-size: 10px; margin-left: 8px; | |
| border-radius: 8px; font-weight: 600; vertical-align: middle; | |
| display: inline-block; box-shadow: 0 1px 2px rgba(0,0,0,0.1); | |
| line-height: 1.2; white-space: nowrap; z-index: 1; | |
| } | |
| .gdev-remove { | |
| position: absolute; top: 4px; right: 4px; | |
| width: 18px; height: 18px; border: none; border-radius: 50%; | |
| background: var(--gdev-tag-bg, #e5e7eb); color: var(--gdev-tag-text, #444); | |
| display: flex; justify-content: center; align-items: center; | |
| cursor: pointer; font-size: 10px; z-index: 100; opacity: 0; | |
| transition: opacity 0.2s, background 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.3); | |
| } | |
| .gdev-highlighted-box:hover .gdev-remove { opacity: 1; } | |
| .gdev-remove:hover { background: #ff4757; color: white; } | |
| /* Settings UI Styles */ | |
| :root { | |
| --gdev-ui-bg: #ffffff; | |
| --gdev-ui-text: #1f2937; | |
| --gdev-ui-border: #e5e7eb; | |
| --gdev-ui-input: #f9fafb; | |
| --gdev-ui-btn: #3b82f6; | |
| --gdev-ui-btn-hover: #2563eb; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --gdev-tag-bg: #374151; --gdev-tag-text: #e5e7eb; | |
| --gdev-ui-bg: #1f2937; --gdev-ui-text: #f9fafb; | |
| --gdev-ui-border: #374151; --gdev-ui-input: #111827; | |
| } | |
| } | |
| #gdev-fab { | |
| position: fixed; bottom: 24px; left: 24px; z-index: 999999; | |
| background: var(--gdev-ui-bg); color: var(--gdev-ui-text); | |
| border: 1px solid var(--gdev-ui-border); border-radius: 20px; | |
| padding: 8px 16px; font-size: 13px; font-weight: 600; font-family: sans-serif; | |
| cursor: pointer; box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| display: flex; align-items: center; gap: 6px; transition: transform 0.2s; | |
| } | |
| #gdev-fab:hover { transform: translateY(-2px); } | |
| #gdev-modal-overlay { | |
| position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; | |
| background: rgba(0,0,0,0.5); backdrop-filter: blur(2px); | |
| z-index: 1000000; display: none; justify-content: center; align-items: center; | |
| font-family: sans-serif; | |
| } | |
| #gdev-modal { | |
| background: var(--gdev-ui-bg); color: var(--gdev-ui-text); | |
| width: 90%; max-width: 500px; max-height: 85vh; border-radius: 12px; | |
| box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1); | |
| display: flex; flex-direction: column; overflow: hidden; | |
| } | |
| .gdev-modal-header { | |
| padding: 16px 20px; border-bottom: 1px solid var(--gdev-ui-border); | |
| display: flex; justify-content: space-between; align-items: center; | |
| } | |
| .gdev-modal-header h2 { margin: 0; font-size: 18px; } | |
| .gdev-close-x { cursor: pointer; font-size: 20px; color: #888; background: none; border: none; } | |
| .gdev-close-x:hover { color: #ff4757; } | |
| .gdev-modal-body { padding: 20px; overflow-y: auto; flex-grow: 1; } | |
| .gdev-input-group { display: flex; gap: 8px; margin-bottom: 20px; } | |
| .gdev-input-group select, .gdev-input-group input { | |
| padding: 8px 12px; border-radius: 6px; border: 1px solid var(--gdev-ui-border); | |
| background: var(--gdev-ui-input); color: var(--gdev-ui-text); outline: none; | |
| } | |
| .gdev-input-group input { flex-grow: 1; } | |
| .gdev-btn { | |
| background: var(--gdev-ui-btn); color: white; border: none; | |
| padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: 600; | |
| } | |
| .gdev-btn:hover { background: var(--gdev-ui-btn-hover); } | |
| .gdev-list-item { | |
| display: flex; justify-content: space-between; align-items: center; | |
| padding: 8px 12px; background: var(--gdev-ui-input); | |
| border-radius: 6px; margin-bottom: 6px; font-size: 13px; | |
| } | |
| .gdev-list-cat { font-size: 10px; font-weight: bold; background: var(--gdev-ui-border); padding: 2px 6px; border-radius: 4px; margin-right: 8px; } | |
| .gdev-delete-btn { color: #ff4757; background: none; border: none; cursor: pointer; font-weight: bold; font-size: 14px; } | |
| .gdev-modal-footer { | |
| padding: 16px 20px; border-top: 1px solid var(--gdev-ui-border); | |
| display: flex; justify-content: flex-end; gap: 10px; | |
| } | |
| .gdev-reload-btn { background: #10b981; } | |
| .gdev-reload-btn:hover { background: #059669; } | |
| `; | |
| document.head.appendChild(style); | |
| }; | |
| /********************************************************************** | |
| * 2. SETTINGS, COLORS & CUSTOM STORAGE | |
| **********************************************************************/ | |
| const SETTINGS = { | |
| blacklist: true, repos: true, reference: true, recommend: true, | |
| security: true, privacy: true, tools: true, courses: true, | |
| search_engines: true, books: true, practice: true, media: true, | |
| public: true, forum: true | |
| }; | |
| const COLORS = { | |
| repos: "rgba(142, 68, 173, 0.12)", reference: "rgba(52, 152, 219, 0.12)", | |
| recommend: "rgba(46, 204, 113, 0.12)", security: "rgba(231, 76, 60, 0.12)", | |
| privacy: "rgba(155, 89, 182, 0.12)", tools: "rgba(241, 196, 15, 0.12)", | |
| courses: "rgba(0, 206, 209, 0.12)", search_engines: "rgba(255, 105, 180, 0.12)", | |
| books: "rgba(139, 69, 19, 0.12)", practice: "rgba(211, 84, 0, 0.12)", | |
| media: "rgba(149, 165, 166, 0.12)", public: "rgba(189, 195, 199, 0.12)", | |
| forum: "rgba(230, 126, 34, 0.12)" | |
| }; | |
| // Load custom sites from localStorage | |
| const STORAGE_KEY = 'gdev_custom_sites_v1'; | |
| let customSites = {}; | |
| try { | |
| const stored = localStorage.getItem(STORAGE_KEY); | |
| if (stored) customSites = JSON.parse(stored); | |
| } catch(e) {} | |
| /********************************************************************** | |
| * 3. DOMAIN DATABASE (Merged with Custom Sites -> O(1) Map) | |
| **********************************************************************/ | |
| const SITES = { | |
| repos: ["github.com", "gitlab.com", "bitbucket.org", "gist.github.com", "npmjs.com", "npmjs.org", "pypi.org", "crates.io", "hub.docker.com", "pkg.go.dev", "sourceforge.net", "launchpad.net", "git.kernel.org", "gitea.com", "sr.ht", "packagist.org", "rubygems.org", "maven.apache.org", "gcr.io", "quay.io", "ghcr.io", "helm.sh", "artifacthub.io"], | |
| reference: ["developer.mozilla.org", "mdn.io", "docs.microsoft.com", "learn.microsoft.com", "rust-lang.org", "python.org", "postgresql.org", "wiki.osdev.org", "os.phil-opp.com", "attack.mitre.org", "php.net", "ruby-doc.org", "cppreference.com", "cplusplus.com", "docs.oracle.com", "reactjs.org", "react.dev", "vuejs.org", "angular.io", "laravel.com", "symfony.com", "django-project.com", "flask.palletsprojects.com", "kubernetes.io", "aws.amazon.com", "cloud.google.com", "azure.microsoft.com", "nginx.org", "httpd.apache.org", "developer.apple.com", "developer.android.com", "golang.org", "kotlinlang.org", "swift.org", "scala-lang.org", "haskell.org", "elixir-lang.org", "mariadb.org", "mysql.com", "sqlite.org", "redis.io", "mongodb.com", "elastic.co", "w3schools.com", "caniuse.com", "nodejs.org", "bun.sh", "deno.land", "tailwindcss.com", "getbootstrap.com", "mui.com", "chakra-ui.com", "ant.design", "swagger.io", "openapi.tools", "electronjs.org", "tauri.app", "flutter.dev", "dart.dev", "unity.com", "unrealengine.com", "godotengine.org", "arduino.cc", "raspberrypi.org", "kali.org", "ubuntu.com", "archlinux.org", "wiki.archlinux.org", "developer.chrome.com", "webkit.org", "graphql.org", "apollographql.com", "prisma.io"], | |
| recommend: ["roadmap.sh", "refactoring.guru", "devdocs.io", "devhints.io", "libhunt.com", "alternativeto.net", "awesome-selfhosted.com", "digitalocean.com", "freecodecamp.org", "css-tricks.com", "smashingmagazine.com", "alistapart.com", "sitepoint.com", "web.dev", "html5rocks.com", "martinfowler.com", "kentcdodds.com", "overreacted.io", "frontendmasters.com", "cyberciti.biz", "thegeekstuff.com", "bytebytego.com", "architecturenotes.co", "highscalability.com"], | |
| security: ["hacktricks.xyz", "book.hacktricks.xyz", "hacktricks.wiki", "ired.team", "portswigger.net", "ctf101.org", "vx-underground.org", "gtfobins.github.io", "lolbas-project.github.io", "exploit-db.com", "vulnhub.com", "pentesterlab.com", "null-byte.wonderhowto.com", "osintframework.com", "bellingcat.com", "virustotal.com", "urlscan.io", "abuseipdb.com", "securitytrails.com", "joesandbox.com", "any.run", "shodan.io", "censys.io", "zoomeye.org", "greynoise.io", "hackerfactor.com", "haveibeenpwned.com", "grep.app", "owasp.org", "cvedetails.com", "rapid7.com", "nmap.org", "wireshark.org", "offensive-security.com", "hackerone.com", "bugcrowd.com", "darkreading.com", "thehackernews.com", "krebsonsecurity.com", "schneier.com", "bleepingcomputer.com", "troyhunt.com", "malwarebytes.com", "kaspersky.com", "crowdstrike.com", "mandiant.com", "mitre.org", "cisa.gov", "ncsc.gov.uk"], | |
| privacy: ["privacyguides.org", "privacytools.io", "anonymousplanet.org", "browserleaks.com", "eff.org", "torproject.org", "signal.org", "protonmail.com", "mullvad.net", "privacyinternational.org", "epic.org", "noyb.eu"], | |
| tools: ["godbolt.org", "dogbolt.org", "regex-generator.olafneumann.org", "codebeautify.org", "openvim.com", "cmdchallenge.com", "jwt.io", "base64decode.org", "jsonlint.com", "jsfiddle.net", "codepen.io", "codesandbox.io", "replit.com", "pastebin.com", "hastebin.com", "regex101.com", "regexr.com", "crontab.guru", "gitignore.io", "postman.com", "insomnia.rest", "figma.com", "draw.io", "diagrams.net", "carbon.now.sh", "ngrok.com", "vercel.com", "netlify.com", "heroku.com", "gchq.github.io", "cyberchef.org", "excalidraw.com", "tldraw.com", "webhook.site", "httpbin.org", "hoppscotch.io", "ray.so", "asciinema.org", "mermaid.live"], | |
| courses: ["khanacademy.org", "codecademy.com", "sololearn.com", "teachyourselfcs.com", "udemy.com", "coursera.org", "edx.org", "pluralsight.com", "datacamp.com", "teamtreehouse.com", "udacity.com", "scrimba.com", "egghead.io", "laracasts.com", "brilliant.org", "tutorialspoint.com", "javatpoint.com", "geeksforgeeks.org", "programiz.com", "w3resource.com", "codecrafters.io", "educative.io", "boot.dev", "epicreact.dev", "totaltypescript.com"], | |
| search_engines: ["you.com", "perplexity.ai", "phind.com", "metager.org", "qwant.com", "duckduckgo.com", "kagi.com", "brave.com", "startpage.com", "ecosia.org", "wolframalpha.com"], | |
| books: ["bottomupcs.com", "beej.us", "cses.fi", "visualgo.net", "algs4.cs.princeton.edu", "oreilly.com", "manning.com", "packtpub.com", "apress.com", "nostarch.com", "pragprog.com", "leanpub.com", "gutenberg.org", "pdfdrive.com"], | |
| practice: ["exercism.org", "codewars.com", "hackthebox.com", "tryhackme.com", "overthewire.org", "leetcode.com", "hackerrank.com", "topcoder.com", "codeforces.com", "atcoder.jp", "spoj.com", "projecteuler.net", "codingbat.com", "edabit.com", "cyberdefenders.org", "root-me.org", "picoctf.org", "neetcode.io", "algoexpert.io", "adventofcode.com", "cssbattle.dev", "frontendmentor.io", "cryptohack.org", "kaggle.com"], | |
| media: ["videocopilot.net", "texturelabs.org", "greyscalegorilla.com", "twitch.tv", "vimeo.com"], | |
| public: ["qiita.com", "dev.to", "hashnode.com", "zenn.dev", "hatenablog.com", "note.com", "dzone.com", "infoq.com", "hackernoon.com", "theregister.com", "arstechnica.com", "techcrunch.com", "wired.com", "towardsdatascience.com"], | |
| forum: ["reddit.com", "old.reddit.com", "m.reddit.com", "stackexchange.com", "stackoverflow.com", "medium.com", "news.ycombinator.com", "lobste.rs", "xda-developers.com", "superuser.com", "serverfault.com", "askubuntu.com", "mathoverflow.net", "crossvalidated.com", "c-sharpcorner.com", "codeproject.com", "forums.asp.net", "community.oracle.com", "groups.google.com", "bbs.archlinux.org", "ubuntuforums.org", "indiehackers.com", "macrumors.com", "tomshardware.com", "linustechtips.com"], | |
| blacklist: ["softonic.com", "dll-files.com", "systweak.com", "chip.de", "reviversoft.com", "tutorialmore.com", "voidcc.com", "it-swarm.dev", "pinterest.com", "pinterest.co.uk", "pinterest.ca", "experts-exchange.com", "stackoverrun.com", "qastack.jp", "programmersought.com", "grepper.com", "codegrepper.com", "hotexamples.com", "cpp.sh", "studytonight.com", "guru99.com", "educba.com", "simplilearn.com", "techbeamers.com", "linuxhint.com", "w3cub.com", "codedojo.com"] | |
| }; | |
| // MERGE Custom Sites into Core Database | |
| for (const [cat, domains] of Object.entries(customSites)) { | |
| if (!SITES[cat]) SITES[cat] = []; | |
| SITES[cat].push(...domains); | |
| } | |
| // Pre-build O(1) Map for speed | |
| const DOMAIN_MAP = new Map(); | |
| for (const [category, domains] of Object.entries(SITES)) { | |
| if (SETTINGS[category]) { | |
| for (const domain of domains) { | |
| DOMAIN_MAP.set(domain, category); | |
| } | |
| } | |
| } | |
| /********************************************************************** | |
| * 4. HIGH-PERFORMANCE CORE LOGIC | |
| **********************************************************************/ | |
| function getHost(a) { | |
| let rawUrl = null; | |
| if (a.href && a.href.startsWith("http") && !a.href.includes("google.com/search")) { | |
| return a.hostname.replace(/^www\./, ""); | |
| } | |
| const href = a.getAttribute("href"); | |
| if (href && href.startsWith("/url?")) rawUrl = new URLSearchParams(href.split("?")[1]).get("q"); | |
| if (!rawUrl) rawUrl = a.getAttribute("data-url"); | |
| if (!rawUrl) { | |
| const dved = a.getAttribute("data-ved") || ""; | |
| const m = dved.match(/https?:\/\/[^\s"'<>]+/); | |
| if (m) rawUrl = m[0]; | |
| } | |
| if (rawUrl && rawUrl.startsWith("http")) { | |
| try { return new URL(rawUrl).hostname.replace(/^www\./, ""); } catch (e) {} | |
| } | |
| return ""; | |
| } | |
| function classify(host) { | |
| if (!host) return null; | |
| if (DOMAIN_MAP.has(host)) return DOMAIN_MAP.get(host); | |
| const parts = host.split('.'); | |
| while (parts.length > 1) { | |
| parts.shift(); | |
| const sub = parts.join('.'); | |
| if (DOMAIN_MAP.has(sub)) return DOMAIN_MAP.get(sub); | |
| } | |
| return null; | |
| } | |
| function getBox(el) { | |
| const box = el.closest(`div.g, div.MjjYud, div.xpd, div[role="listitem"], div.c3mZkd, div.d5oMvf, div.AuVD, div.P8ujBc, div.Wt5Tfe, div.VwiC3b`); | |
| if (box) return box; | |
| const fallback = el.closest('div[data-ved], li'); | |
| if (fallback && fallback.querySelectorAll('a[href^="http"]').length > 3) return el.closest('div, span'); | |
| return fallback; | |
| } | |
| /********************************************************************** | |
| * 5. DOM MANIPULATION | |
| **********************************************************************/ | |
| function processDOM(root = document) { | |
| const links = root.tagName === 'A' ? [root] : root.querySelectorAll('a[href]'); | |
| for (let i = 0; i < links.length; i++) { | |
| const a = links[i]; | |
| if (a.dataset.gdev) continue; | |
| a.dataset.gdev = '1'; | |
| const host = getHost(a); | |
| if (!host) continue; | |
| const cat = classify(host); | |
| if (!cat) continue; | |
| const box = getBox(a); | |
| if (!box) continue; | |
| if (cat === "blacklist") { | |
| box.style.display = 'none'; | |
| continue; | |
| } | |
| if (!box.dataset.gdevHighlighted) { | |
| box.classList.add('gdev-highlighted-box'); | |
| box.style.setProperty("background-color", COLORS[cat], "important"); | |
| box.style.setProperty("border-radius", "8px", "important"); | |
| if (getComputedStyle(box).position === 'static') box.style.position = "relative"; | |
| box.dataset.gdevHighlighted = '1'; | |
| const btn = document.createElement("button"); | |
| btn.className = "gdev-remove"; btn.innerHTML = "✕"; btn.title = "Hide result"; | |
| btn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); box.style.display = 'none'; }; | |
| box.appendChild(btn); | |
| } | |
| if (!box.dataset.gdevTagged) { | |
| const s = document.createElement("span"); | |
| s.className = "gdev-tag"; | |
| s.textContent = cat.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); | |
| const h3 = box.querySelector("h3"); | |
| const domainTextWrapper = box.querySelector(".VuuXrf"); | |
| if (h3) h3.appendChild(s); | |
| else if (domainTextWrapper && domainTextWrapper.parentElement) domainTextWrapper.parentElement.appendChild(s); | |
| else a.appendChild(s); | |
| box.dataset.gdevTagged = '1'; | |
| } | |
| } | |
| } | |
| /********************************************************************** | |
| * 6. BUILT-IN SETTINGS UI MANAGER | |
| **********************************************************************/ | |
| function initUI() { | |
| if (document.getElementById('gdev-fab')) return; | |
| // 1. Create Floating Button | |
| const fab = document.createElement('div'); | |
| fab.id = 'gdev-fab'; | |
| fab.innerHTML = '⚙️ DevSearch'; | |
| document.body.appendChild(fab); | |
| // 2. Create Modal Template | |
| const overlay = document.createElement('div'); | |
| overlay.id = 'gdev-modal-overlay'; | |
| overlay.innerHTML = ` | |
| <div id="gdev-modal" onclick="event.stopPropagation()"> | |
| <div class="gdev-modal-header"> | |
| <h2>Custom Domains</h2> | |
| <button class="gdev-close-x" id="gdev-close-x">×</button> | |
| </div> | |
| <div class="gdev-modal-body"> | |
| <div class="gdev-input-group"> | |
| <select id="gdev-cat-select"> | |
| ${Object.keys(SITES).map(c => `<option value="${c}">${c}</option>`).join('')} | |
| </select> | |
| <input type="text" id="gdev-domain-input" placeholder="e.g. example.com"> | |
| <button class="gdev-btn" id="gdev-add-btn">Add</button> | |
| </div> | |
| <div id="gdev-custom-list"></div> | |
| </div> | |
| <div class="gdev-modal-footer"> | |
| <button class="gdev-btn" id="gdev-close-btn">Close</button> | |
| <button class="gdev-btn gdev-reload-btn" id="gdev-reload-btn" style="display:none;">Save & Reload</button> | |
| </div> | |
| </div> | |
| `; | |
| document.body.appendChild(overlay); | |
| let requiresReload = false; | |
| const renderList = () => { | |
| const list = document.getElementById('gdev-custom-list'); | |
| list.innerHTML = ''; | |
| for (const [cat, domains] of Object.entries(customSites)) { | |
| for (const d of domains) { | |
| const item = document.createElement('div'); | |
| item.className = 'gdev-list-item'; | |
| item.innerHTML = ` | |
| <div><span class="gdev-list-cat">${cat}</span> <span>${d}</span></div> | |
| <button class="gdev-delete-btn" data-cat="${cat}" data-domain="${d}">×</button> | |
| `; | |
| list.appendChild(item); | |
| } | |
| } | |
| // Delete Handlers | |
| document.querySelectorAll('.gdev-delete-btn').forEach(btn => { | |
| btn.onclick = (e) => { | |
| const c = e.target.getAttribute('data-cat'); | |
| const d = e.target.getAttribute('data-domain'); | |
| customSites[c] = customSites[c].filter(x => x !== d); | |
| if (customSites[c].length === 0) delete customSites[c]; | |
| localStorage.setItem(STORAGE_KEY, JSON.stringify(customSites)); | |
| requiresReload = true; | |
| document.getElementById('gdev-reload-btn').style.display = 'block'; | |
| renderList(); | |
| }; | |
| }); | |
| }; | |
| const extractDomain = (input) => { | |
| try { | |
| let s = input.toLowerCase().trim(); | |
| if (!s.startsWith('http')) s = 'https://' + s; | |
| return new URL(s).hostname.replace(/^www\./, ''); | |
| } catch (e) { | |
| return input.toLowerCase().trim().replace(/^www\./, ''); | |
| } | |
| }; | |
| // Add Handler | |
| document.getElementById('gdev-add-btn').onclick = () => { | |
| const cat = document.getElementById('gdev-cat-select').value; | |
| const raw = document.getElementById('gdev-domain-input').value; | |
| const domain = extractDomain(raw); | |
| if (!domain || domain.includes('google.com')) return alert("Invalid domain"); | |
| if (!customSites[cat]) customSites[cat] = []; | |
| if (!customSites[cat].includes(domain)) { | |
| customSites[cat].push(domain); | |
| localStorage.setItem(STORAGE_KEY, JSON.stringify(customSites)); | |
| requiresReload = true; | |
| document.getElementById('gdev-reload-btn').style.display = 'block'; | |
| document.getElementById('gdev-domain-input').value = ''; | |
| renderList(); | |
| } | |
| }; | |
| const closeModal = () => { overlay.style.display = 'none'; }; | |
| fab.onclick = () => { | |
| renderList(); | |
| overlay.style.display = 'flex'; | |
| }; | |
| overlay.onclick = closeModal; | |
| document.getElementById('gdev-close-x').onclick = closeModal; | |
| document.getElementById('gdev-close-btn').onclick = closeModal; | |
| document.getElementById('gdev-reload-btn').onclick = () => location.reload(); | |
| } | |
| /********************************************************************** | |
| * 7. HIGH-SPEED OBSERVER | |
| **********************************************************************/ | |
| injectStyles(); | |
| initUI(); | |
| processDOM(document); | |
| let pendingNodes = new Set(); | |
| let frameRequested = false; | |
| new MutationObserver((mutations) => { | |
| for (const m of mutations) { | |
| for (const node of m.addedNodes) { | |
| if (node.nodeType === 1) pendingNodes.add(node); | |
| } | |
| } | |
| if (pendingNodes.size > 0 && !frameRequested) { | |
| frameRequested = true; | |
| requestAnimationFrame(() => { | |
| for (const node of pendingNodes) processDOM(node); | |
| pendingNodes.clear(); | |
| frameRequested = false; | |
| }); | |
| } | |
| }).observe(document.body, { childList: true, subtree: true }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment