Google Site Restrictor (SS64/Microsoft/VMware)

Alleen resultaten van ss64.com, microsoft.com en vmware.com tonen (met toggle).

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Google Site Restrictor (SS64/Microsoft/VMware)
// @namespace    https://github.com/SeppeHeyvaert
// @version      2.0.0
// @description  Alleen resultaten van ss64.com, microsoft.com en vmware.com tonen (met toggle).
// @author       SeppeHeyvaert
// @match        https://www.google.com/*
// @match        https://www.google.*/*
// @match        https://google.com/*
// @match        https://google.be/*
// @match        https://www.google.be/*
// @run-at       document-start
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(() => {
  'use strict';

  /* ---------- Config ---------- */
  const DOMAINS = ['ss64.com', 'microsoft.com', 'vmware.com'];
  const FILTER_STRING = '(site:ss64.com OR site:microsoft.com OR site:vmware.com)';
  const STATE_KEY = 'gsr_enabled';

  /* ---------- State & Menu ---------- */
  let enabled = GM_getValue(STATE_KEY, true);

  function updateMenu() {
    GM_registerMenuCommand(
      `Google Site Restrictor: ${enabled ? 'UIT' : 'AAN'} zetten`,
      () => {
        enabled = !enabled;
        GM_setValue(STATE_KEY, enabled);
        console.log('[GSR] Enabled =', enabled);
        // Bij togglen: herfilter DOM, maar query niet herschrijven om flikkeren te beperken
        filterResults();
        markActive();
      },
      { id: 'gsrToggle', autoReload: false }
    );
  }

  updateMenu();

  /* ---------- Helpers ---------- */
  const hasOurFilter = (q) => {
    if (!q) return false;
    const lc = q.toLowerCase();
    return lc.includes('site:ss64.com') ||
           lc.includes('site:microsoft.com') ||
           lc.includes('site:vmware.com');
  };

  function addFilter(q) {
    q = (q || '').trim();
    if (hasOurFilter(q)) return q;
    return q.length ? `${q} ${FILTER_STRING}` : FILTER_STRING;
  }

  function isAllowedHost(host) {
    return DOMAINS.some(d => host === d || host.endsWith('.' + d));
  }

  function realHref(href) {
    try {
      if (href.startsWith('/url?')) {
        const sp = new URLSearchParams(href.slice(5));
        return sp.get('q') || sp.get('url') || href;
      }
      return href;
    } catch {
      return href;
    }
  }

  function hostFrom(href) {
    try {
      return new URL(href, location.origin).hostname || '';
    } catch {
      return '';
    }
  }

  function markActive() {
    document.documentElement.setAttribute('data-gsr-active', enabled ? '1' : '0');
  }

  function injectStyle() {
    if (document.getElementById('gsr-style')) return;
    const st = document.createElement('style');
    st.id = 'gsr-style';
    st.textContent = `
      html[data-gsr-active="1"] .gsr-hide { display: none !important; }
    `;
    document.documentElement.appendChild(st);
  }

  /* ---------- Query Restrict ---------- */
  function restrictQueryIfNeeded() {
    if (!enabled) return;
    try {
      const url = new URL(location.href);
      if (!url.pathname.startsWith('/search')) return;
      const q = url.searchParams.get('q') || '';
      if (!hasOurFilter(q)) {
        url.searchParams.set('q', addFilter(q));
        // echte navigatie zodat Google de filter gebruikt
        location.replace(url.toString());
      }
    } catch {}
  }

  /* ---------- DOM Filtering ---------- */
  function resultCards() {
    // H3 elementen binnen links zijn stabiel genoeg
    const h3s = [...document.querySelectorAll('#search h3, #rso h3')];
    return h3s.map(h3 => h3.closest('a[href]')).filter(Boolean);
  }

  function filterResults() {
    const links = resultCards();
    for (const a of links) {
      const href = realHref(a.getAttribute('href') || '');
      const host = hostFrom(href);
      const card =
        a.closest('div.g') ||
        a.closest('div.MjjYud') ||
        a.closest('div.yuRUbf') ||
        a.closest('div[data-hveid]') ||
        a.closest('div[jsname]') ||
        a.closest('div');

      if (!card) continue;

      if (enabled) {
        if (!isAllowedHost(host)) {
          card.classList.add('gsr-hide');
        } else {
          card.classList.remove('gsr-hide');
        }
      } else {
        card.classList.remove('gsr-hide');
      }
    }
  }

  /* ---------- Form Hook ---------- */
  function hookForms() {
    document.addEventListener('submit', e => {
      if (!enabled) return;
      const form = e.target;
      if (!(form instanceof HTMLFormElement)) return;
      const action = form.getAttribute('action') || '';
      if (!/\/search/i.test(action)) return;
      const qInput = form.querySelector('input[name="q"], textarea[name="q"]');
      if (qInput) {
        qInput.value = addFilter(qInput.value);
      }
    }, true);
  }

  /* ---------- History Hook (SPA navigaties) ---------- */
  function hookHistory() {
    const wrap = m => {
      const orig = history[m];
      return function () {
        const r = orig.apply(this, arguments);
        queueMicrotask(() => {
          restrictQueryIfNeeded();
          filterResults();
        });
        return r;
      };
    };
    history.pushState = wrap('pushState');
    history.replaceState = wrap('replaceState');
    window.addEventListener('popstate', () => {
      restrictQueryIfNeeded();
      filterResults();
    });
  }

  /* ---------- Mutation Observer ---------- */
  function observe() {
    const mo = new MutationObserver(filterResults);
    mo.observe(document.documentElement, { childList: true, subtree: true });
  }

  /* ---------- Init ---------- */
  function init() {
    injectStyle();
    markActive();
    restrictQueryIfNeeded();
    filterResults();
  }

  hookForms();
  hookHistory();
  observe();

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }

})();