MediaWiki:Common.js

From B-Wiki
Revision as of 23:26, 14 August 2026 by Tinel (talk | contribs) (Beautify PIN unlock viewer (homecoming styles + hero))
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Any JavaScript here will be loaded for all users on every page load. */
/* === FAMILY_TRIP_PIN_GATE_START === */
(function () {
  var CIPHER_ID = "family-trip-cipher-v3";

  function b64ToBytes(b64) {
    var bin = atob(b64);
    var out = new Uint8Array(bin.length);
    for (var i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
    return out;
  }

  async function deriveKey(pin, salt) {
    var enc = new TextEncoder();
    var baseKey = await crypto.subtle.importKey("raw", enc.encode(pin), "PBKDF2", false, ["deriveKey"]);
    return crypto.subtle.deriveKey(
      { name: "PBKDF2", salt: salt, iterations: 200000, hash: "SHA-256" },
      baseKey,
      { name: "AES-GCM", length: 256 },
      false,
      ["decrypt"]
    );
  }

  function renderHtml(out, html) {
    while (out.firstChild) out.removeChild(out.firstChild);
    var frame = document.createElement("iframe");
    frame.id = "family-trip-frame";
    frame.title = "Trip to Berlin 2026";
    frame.style.cssText = "width:100%;min-height:80vh;border:0;border-radius:12px;box-shadow:0 12px 40px rgba(15,23,42,.18);background:#fff;";
    frame.setAttribute("sandbox", "allow-same-origin allow-popups allow-popups-to-escape-sandbox");

    var doc =
      "<!DOCTYPE html><html><head><meta charset=\"utf-8\">" +
      "<base href=\"https://bogza.ro/\">" +
      "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" +
      "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">" +
      "<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>" +
      "<link href=\"https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=Source+Sans+3:wght@400;600;700&display=swap\" rel=\"stylesheet\">" +
      "<link rel=\"stylesheet\" href=\"https://bogza.ro/load.php?lang=en&modules=site.styles%7Cskins.vector.styles.legacy&only=styles&skin=vector\">" +
      "<style>" +
      ":root{--ink:#1a2332;--muted:#5b6b7c;--line:#d7dee7;--card:#ffffff;--bg:#eef3f8;--brand:#1f6aa5;--ok:#1b7f4e;--soft:#f7fafc;}" +
      "html,body{margin:0;padding:0;background:var(--bg);color:var(--ink);font-family:'Source Sans 3',Segoe UI,sans-serif;line-height:1.55;}" +
      "body{padding:20px 16px 48px;}" +
      ".trip-shell{max-width:980px;margin:0 auto;}" +
      ".hero{background:linear-gradient(135deg,#1f6aa5 0%,#2f8f6b 55%,#f0b429 130%);color:#fff;border-radius:18px;padding:22px 24px;margin:0 0 18px;box-shadow:0 16px 40px rgba(31,106,165,.28);}" +
      ".hero h1{font-family:Fraunces,Georgia,serif;font-size:1.7rem;line-height:1.2;margin:0 0 8px;font-weight:700;}" +
      ".hero p{margin:0;opacity:.95;font-size:1.02rem;}" +
      ".mw-parser-output,.mw-content-ltr{background:transparent;}" +
      ".mw-parser-output > .mw-heading1:first-child{display:none;}" +
      "h1,h2,h3,h4{font-family:Fraunces,Georgia,serif;color:#16324a;line-height:1.25;}" +
      "h2{border-bottom:2px solid #d9e7f2;padding-bottom:.25em;margin-top:1.6em;}" +
      "a{color:var(--brand);}" +
      "table{border-collapse:separate;border-spacing:0;width:100%;margin:1em 0;background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden;box-shadow:0 6px 18px rgba(15,23,42,.06);}" +
      "th,td{border-bottom:1px solid var(--line);border-right:1px solid var(--line);padding:10px 12px;vertical-align:top;}" +
      "tr > :last-child{border-right:0;}" +
      "tr:last-child > *{border-bottom:0;}" +
      "th{background:#e9f2fa;font-weight:700;color:#184866;}" +
      ".wikitable{background:var(--card);}" +
      "img{max-width:100%;height:auto;border-radius:10px;}" +
      "ul,ol{padding-left:1.25em;}" +
      "pre,code{font-family:ui-monospace,Consolas,monospace;}" +
      "pre{background:#0f172a;color:#e2e8f0;border-radius:10px;padding:12px;overflow:auto;}" +
      ".gallery{display:flex;flex-wrap:wrap;gap:10px;}" +
      ".gallerybox{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:8px;box-shadow:0 4px 14px rgba(15,23,42,.05);}" +
      ".thumbinner{border:0 !important;background:transparent !important;padding:0 !important;}" +
      "</style></head><body><div class=\"trip-shell\">" +
      "<div class=\"hero\"><h1>Trip to Berlin 2026</h1><p>Family road journal · PIN unlocked · Welcome home to Iași</p></div>" +
      html +
      "</div></body></html>";

    frame.srcdoc = doc;
    out.appendChild(frame);
    frame.addEventListener("load", function () {
      try {
        var h = frame.contentDocument.documentElement.scrollHeight;
        if (h && h > 400) frame.style.height = Math.min(h + 48, 16000) + "px";
      } catch (e) {}
    });
  }

  function mount() {
    var el = document.getElementById(CIPHER_ID);
    if (!el || el.getAttribute("data-ready") === "1") return;
    el.setAttribute("data-ready", "1");
    var pack;
    try { pack = JSON.parse(el.textContent); } catch (e) { return; }

    var gate = document.createElement("div");
    gate.id = "family-trip-gate-v3";
    gate.style.cssText = "max-width:560px;margin:2em auto;padding:1.5em 1.6em;border:0;background:linear-gradient(180deg,#ffffff,#f3f7fb);border-radius:16px;font-family:Segoe UI,sans-serif;box-shadow:0 18px 40px rgba(15,23,42,.12);";
    gate.innerHTML =
      "<div style=\"font-size:2rem;margin-bottom:6px\">🏠🔒</div>" +
      "<h2 style=\"margin:0 0 8px;color:#16324a\">Family trip journal</h2>" +
      "<p style=\"color:#5b6b7c;margin:0 0 14px\">PIN-protected memories from Iași → Berlin → home. Enter the family PIN to open the finished itinerary.</p>" +
      "<label for=\"family-trip-pin-v3\" style=\"font-weight:600;color:#184866\">Family PIN</label><br>" +
      "<input id=\"family-trip-pin-v3\" type=\"password\" inputmode=\"numeric\" autocomplete=\"off\" " +
      "style=\"font-size:1.25em;letter-spacing:0.25em;padding:10px 12px;width:11em;margin:10px 0;border:1px solid #c5d3e0;border-radius:10px\" /> " +
      "<button id=\"family-trip-unlock-v3\" type=\"button\" style=\"padding:10px 16px;border:0;border-radius:999px;background:#1f6aa5;color:#fff;font-weight:700;cursor:pointer\">Unlock</button>" +
      "<div id=\"family-trip-err-v3\" style=\"color:#b32424;margin-top:10px;min-height:1.2em\"></div>";

    var out = document.createElement("div");
    out.id = "family-trip-out-v3";
    out.style.cssText = "display:none;margin:1em 0 2em;";

    el.style.display = "none";
    el.parentNode.insertBefore(gate, el);
    el.parentNode.insertBefore(out, el.nextSibling);

    async function unlock() {
      var err = document.getElementById("family-trip-err-v3");
      var btn = document.getElementById("family-trip-unlock-v3");
      err.textContent = "";
      var pin = document.getElementById("family-trip-pin-v3").value.trim();
      if (pin.length < 4) { err.textContent = "PIN too short."; return; }
      btn.disabled = true;
      err.textContent = "Unlocking…";
      try {
        var key = await deriveKey(pin, b64ToBytes(pack.salt));
        var pt = await crypto.subtle.decrypt(
          { name: "AES-GCM", iv: b64ToBytes(pack.nonce), tagLength: 128 },
          key,
          b64ToBytes(pack.ciphertext)
        );
        var html = new TextDecoder().decode(pt);
        if (pack.fmt !== "html") {
          err.textContent = "Unexpected package format.";
          btn.disabled = false;
          return;
        }
        if (html.indexOf("&lt;table") !== -1 && html.indexOf("<table") === -1) {
          var ta = document.createElement("textarea");
          ta.innerHTML = html;
          html = ta.value;
        }
        renderHtml(out, html);
        out.style.display = "block";
        gate.style.display = "none";
      } catch (e) {
        err.textContent = "Wrong PIN.";
        btn.disabled = false;
      }
    }

    document.getElementById("family-trip-unlock-v3").addEventListener("click", unlock);
    document.getElementById("family-trip-pin-v3").addEventListener("keydown", function (ev) {
      if (ev.key === "Enter") unlock();
    });
  }

  if (window.mw && mw.hook) mw.hook("wikipage.content").add(function () { mount(); });
  else document.addEventListener("DOMContentLoaded", mount);
})();
/* === FAMILY_TRIP_PIN_GATE_END === */