MediaWiki:Common.js: Difference between revisions

From B-Wiki
Jump to navigation Jump to search
Created page with "Any JavaScript here will be loaded for all users on every page load.: var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof W..."
 
m Fix syntax error and enable dynamic trip summaries (PIN gate v3)
 
(12 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* Any JavaScript here will be loaded for all users on every page load. */
/* Any JavaScript here will be loaded for all users on every page load. */
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,a=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,a.util.encode(e.content),e.alias):"Array"===a.util.type(e)?e.map(a.util.encode):e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function(e){var t=a.util.type(e);switch(t){case"Object":var n={};for(var r in e)e.hasOwnProperty(r)&&(n[r]=a.util.clone(e[r]));return n;case"Array":return e.map&&e.map(function(e){return a.util.clone(e)})}return e}},languages:{extend:function(e,t){var n=a.util.clone(a.languages[e]);for(var r in t)n[r]=t[r];return n},insertBefore:function(e,t,n,r){r=r||a.languages;var i=r[e];if(2==arguments.length){n=arguments[1];for(var s in n)n.hasOwnProperty(s)&&(i[s]=n[s]);return i}var l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==t)for(var s in n)n.hasOwnProperty(s)&&(l[s]=n[s]);l[o]=i[o]}return a.languages.DFS(a.languages,function(t,a){a===r[e]&&t!=e&&(this[t]=l)}),r[e]=l},DFS:function(e,t,n,r){r=r||{};for(var i in e)e.hasOwnProperty(i)&&(t.call(e,i,e[i],n||i),"Object"!==a.util.type(e[i])||r[a.util.objId(e[i])]?"Array"!==a.util.type(e[i])||r[a.util.objId(e[i])]||(r[a.util.objId(e[i])]=!0,a.languages.DFS(e[i],t,i,r)):(r[a.util.objId(e[i])]=!0,a.languages.DFS(e[i],t,null,r)))}},plugins:{},highlightAll:function(e,t){var n={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",n);for(var r,i=n.elements||document.querySelectorAll(n.selector),s=0;r=i[s++];)a.highlightElement(r,e===!0,n.callback)},highlightElement:function(t,n,r){for(var i,s,l=t;l&&!e.test(l.className);)l=l.parentNode;l&&(i=(l.className.match(e)||[,""])[1],s=a.languages[i]),t.className=t.className.replace(e,"").replace(/\s+/g," ")+" language-"+i,l=t.parentNode,/pre/i.test(l.nodeName)&&(l.className=l.className.replace(e,"").replace(/\s+/g," ")+" language-"+i);var o=t.textContent,u={element:t,language:i,grammar:s,code:o};if(a.hooks.run("before-sanity-check",u),!u.code||!u.grammar)return void a.hooks.run("complete",u);if(a.hooks.run("before-highlight",u),n&&_self.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u.highlightedCode=e.data,a.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),a.hooks.run("after-highlight",u),a.hooks.run("complete",u)},c.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=a.highlight(u.code,u.grammar,u.language),a.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(t),a.hooks.run("after-highlight",u),a.hooks.run("complete",u)},highlight:function(e,t,r){var i=a.tokenize(e,t);return n.stringify(a.util.encode(i),r)},tokenize:function(e,t,n){var r=a.Token,i=[e],s=t.rest;if(s){for(var l in s)t[l]=s[l];delete t.rest}e:for(var l in t)if(t.hasOwnProperty(l)&&t[l]){var o=t[l];o="Array"===a.util.type(o)?o:[o];for(var u=0;u<o.length;++u){var c=o[u],g=c.inside,p=!!c.lookbehind,d=!!c.greedy,m=0,f=c.alias;c=c.pattern||c;for(var h=0;h<i.length;h++){var y=i[h];if(i.length>e.length)break e;if(!(y instanceof r)){c.lastIndex=0;var v=c.exec(y),b=1;if(!v&&d&&h!=i.length-1){var k=i[h+1].matchedStr||i[h+1],w=y+k;if(h<i.length-2&&(w+=i[h+2].matchedStr||i[h+2]),c.lastIndex=0,v=c.exec(w),!v)continue;var P=v.index+(p?v[1].length:0);if(P>=y.length)continue;var x=v.index+v[0].length,A=y.length+k.length;if(b=3,A>=x){if(i[h+1].greedy)continue;b=2,w=w.slice(0,A)}y=w}if(v){p&&(m=v[1].length);var P=v.index+m,v=v[0].slice(m),x=P+v.length,j=y.slice(0,P),_=y.slice(x),S=[h,b];j&&S.push(j);var N=new r(l,g?a.tokenize(v,g):v,f,v,d);S.push(N),_&&S.push(_),Array.prototype.splice.apply(i,S)}}}}}return i},hooks:{all:{},add:function(e,t){var n=a.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=a.hooks.all[e];if(n&&n.length)for(var r,i=0;r=n[i++];)r(t)}}},n=a.Token=function(e,t,a,n,r){this.type=e,this.content=t,this.alias=a,this.matchedStr=n||null,this.greedy=!!r};if(n.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===a.util.type(e))return e.map(function(a){return n.stringify(a,t,e)}).join("");var i={type:e.type,content:n.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var s="Array"===a.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,s)}a.hooks.run("wrap",i);var l="";for(var o in i.attributes)l+=(l?" ":"")+o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+l+">"+i.content+"</"+i.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;_self.postMessage(a.highlight(r,a.languages[n],n)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(a.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",a.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism),Prism.languages.markup={comment:/<!--[\w\W]*?-->/,prolog:/<\?[\w\W]+?\?>/,doctype:/<!DOCTYPE[\w\W]+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&amp;/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(<style[\w\W]*?>)[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)),Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(<script[\w\W]*?>)[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript,function(){"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.forEach&&Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var a,n=t.getAttribute("data-src"),r=t,i=/\blang(?:uage)?-(?!\*)(\w+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(a=(t.className.match(i)||[,""])[1]),!a){var s=(n.match(/\.(\w+)$/)||[,""])[1];a=e[s]||s}var l=document.createElement("code");l.className="language-"+a,t.textContent="",l.textContent="Loading…",t.appendChild(l);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(l.textContent=o.responseText,Prism.highlightElement(l)):o.status>=400?l.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:l.textContent="✖ Error: File does not exist or is empty")},o.send(null)})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight))}();
/* === 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 injectLandingStyles() {
    if (document.getElementById("family-trip-landing-css")) return;
    var style = document.createElement("style");
    style.id = "family-trip-landing-css";
    style.textContent = [
      "#content.mw-body, #bodyContent { background: transparent !important; }",
      "#firstHeading { display: none !important; }",
      "#siteSub, #contentSub, #jump-to-nav { display: none !important; }",
      ".family-trip-landing-shell {",
      "  position: relative; overflow: hidden; margin: 0 auto 1.5rem; max-width: 720px;",
      "  border-radius: 28px; padding: 2px;",
      "  background: linear-gradient(135deg, rgba(31,106,165,.55), rgba(47,143,107,.45), rgba(240,180,41,.35));",
      "  box-shadow: 0 28px 60px rgba(22,50,74,.18);",
      "}",
      ".family-trip-landing-inner {",
      "  position: relative; border-radius: 26px; overflow: hidden;",
      "  background:",
      "    radial-gradient(1200px 420px at 10% -10%, rgba(255,255,255,.55), transparent 55%),",
      "    radial-gradient(900px 380px at 110% 0%, rgba(240,180,41,.18), transparent 50%),",
      "    linear-gradient(160deg, #f7fbff 0%, #eef6f2 48%, #f8f4ea 100%);",
      "  padding: 34px 28px 30px; font-family: 'Segoe UI', system-ui, sans-serif; color: #16324a;",
      "}",
      ".family-trip-landing-inner::before {",
      "  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .35;",
      "  background-image: linear-gradient(rgba(22,50,74,.035) 1px, transparent 1px),",
      "   linear-gradient(90deg, rgba(22,50,74,.035) 1px, transparent 1px);",
      " background-size: 28px 28px; mask-image: linear-gradient(180deg, #000, transparent 90%);",
      "}",
      ".ft-kicker { position: relative; display: inline-flex; align-items: center; gap: 8px;",
      "  font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;",
      "  color: #1f6aa5; background: rgba(255,255,255,.72); border: 1px solid rgba(31,106,165,.18);",
      "  border-radius: 999px; padding: 7px 12px; margin-bottom: 16px; }",
      ".ft-title { position: relative; margin: 0 0 10px; font-size: clamp(1.8rem, 4vw, 2.45rem);",
      "  line-height: 1.12; font-weight: 750; letter-spacing: -0.02em; color: #12283a; }",
      ".ft-lead { position: relative; margin: 0 0 22px; max-width: 36rem; color: #4d6275;",
      " font-size: 1.05rem; line-height: 1.55; }",
      ".ft-route { position: relative; display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 22px; }",
      ".ft-chip { display: inline-flex; align-items: center; gap: 6px; border-radius: 999px;",
      "  padding: 8px 12px; background: rgba(255,255,255,.8); border: 1px solid rgba(22,50,74,.08);",
      "  color: #1a3348; font-weight: 650; font-size: .92rem; box-shadow: 0 6px 16px rgba(22,50,74,.05); }",
      ".ft-chip span { opacity: .55; font-weight: 600; }",
      ".ft-meta { position: relative; display: grid; grid-template-columns: repeat(3, minmax(0,1fr));",
      "  gap: 10px; margin: 0 0 24px; }",
      ".ft-stat { background: rgba(255,255,255,.78); border: 1px solid rgba(22,50,74,.07);",
      " border-radius: 16px; padding: 12px 14px; }",
      ".ft-stat b { display: block; font-size: 1.02rem; color: #143248; margin-bottom: 2px; }",
      ".ft-stat span { color: #62768a; font-size: .86rem; }",
      ".ft-unlock { position: relative; background: rgba(255,255,255,.88);",
      " border: 1px solid rgba(31,106,165,.14); border-radius: 20px; padding: 18px 18px 16px;",
      " box-shadow: inset 0 1px 0 rgba(255,255,255,.8), 0 12px 28px rgba(31,106,165,.08); }",
      ".ft-unlock label { display: block; font-weight: 700; color: #184866; margin-bottom: 8px; }",
      ".ft-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }",
      ".ft-row input { flex: 1 1 160px; min-width: 140px; font-size: 1.2rem; letter-spacing: .28em;",
      "  padding: 12px 14px; border-radius: 14px; border: 1px solid #c5d3e0; background: #fff;",
      "  color: #16324a; outline: none; transition: border-color .15s, box-shadow .15s; }",
      ".ft-row input:focus { border-color: #1f6aa5; box-shadow: 0 0 0 4px rgba(31,106,165,.15); }",
      ".ft-row button { appearance: none; border: 0; cursor: pointer; border-radius: 999px;",
      "  padding: 12px 18px; font-weight: 750; color: #fff;",
      " background: linear-gradient(135deg, #1f6aa5, #2f8f6b);",
      "  box-shadow: 0 10px 22px rgba(31,106,165,.28); transition: transform .12s, filter .12s; }",
      ".ft-row button:hover { filter: brightness(1.05); transform: translateY(-1px); }",
      ".ft-row button:disabled { opacity: .7; cursor: wait; transform: none; }",
      ".ft-err { min-height: 1.25em; margin-top: 10px; color: #b32424; font-size: .95rem; }",
      ".ft-note { position: relative; margin: 16px 0 0; color: #6a7d90; font-size: .88rem; }",
      "@media (max-width: 640px) {",
      "  .family-trip-landing-inner { padding: 26px 18px 22px; }",
      " .ft-meta { grid-template-columns: 1fr; }",
      "}",
      "#family-trip-out-v3 { margin-top: 1.25rem; }",
      "#family-trip-frame { width: 100%; min-height: 80vh; border: 0; border-radius: 18px;",
      "  box-shadow: 0 18px 48px rgba(15,23,42,.16); background: #fff; }",
      ".trip-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin: 1.5em 0; }",
      ".trip-photo-card { background: #fff; border: 1px solid #d7dee7; border-radius: 16px; overflow: hidden; box-shadow: 0 6px 18px rgba(15,23,42,.06); }",
      ".trip-photo-card img { width: 100%; height: 240px; object-fit: cover; display: block; cursor: pointer; transition: transform .2s ease; }",
      ".trip-photo-card img:hover { transform: scale(1.02); }",
      ".trip-photo-cap { padding: 12px 14px; font-size: .92rem; color: #334155; line-height: 1.4; border-top: 1px solid #f1f5f9; }"
    ].join("\n");
    document.head.appendChild(style);
  }
 
  function renderHtml(out, html, pack) {
    while (out.firstChild) out.removeChild(out.firstChild);
    var frame = document.createElement("iframe");
    frame.id = "family-trip-frame";
    var tripTitle = (pack && pack.title) ? pack.title : "Family Trip Journal";
    var tripSubtitle = (pack && pack.heroSubtitle) ? pack.heroSubtitle : "Family road journal · PIN unlocked";
    frame.title = tripTitle;
    frame.setAttribute("sandbox", "allow-same-origin allow-popups allow-popups-to-escape-sandbox");
 
    var heroHeader = (pack && pack.noHeroHeader) ? "" : '<div class="hero"><h1>' + tripTitle + '</h1><p>' + tripSubtitle + '</p></div>';
 
    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;}' +
      '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;}' +
      '.trip-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin: 1.5em 0; }' +
      '.trip-photo-card { background: #fff; border: 1px solid #d7dee7; border-radius: 16px; overflow: hidden; box-shadow: 0 6px 18px rgba(15,23,42,.06); }' +
      '.trip-photo-card img { width: 100%; height: 260px; object-fit: cover; display: block; border-radius: 16px 16px 0 0; }' +
      '.trip-photo-cap { padding: 12px 14px; font-size: .92rem; color: #334155; line-height: 1.4; border-top: 1px solid #f1f5f9; }' +
      '.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">' +
      heroHeader +
      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");
    injectLandingStyles();
 
    var pack;
    try { pack = JSON.parse(el.textContent); } catch (e) { return; }
 
    var fallback = document.getElementById("family-trip-landing-fallback");
    if (fallback) fallback.style.display = "none";
 
    var kickerText = (pack && pack.kicker) ? pack.kicker : "Jurnal de vacanță · Criptat";
    var titleText = (pack && pack.title) ? pack.title : "Jurnal de Vacanță";
    var leadText = (pack && pack.lead) ? pack.lead : "Introduceți codul PIN pentru a deschide jurnalul de călătorie și galeria foto.";
 
    var routeHtml = "";
    if (pack && pack.chips && Array.isArray(pack.chips) && pack.chips.length > 0) {
      routeHtml = '<div class="ft-route">' + pack.chips.map(function(c) {
        return '<div class="ft-chip">' + c + '</div>';
      }).join("") + '</div>';
    }
 
    var statsHtml = "";
    if (pack && pack.stats && Array.isArray(pack.stats) && pack.stats.length > 0) {
      statsHtml = '<div class="ft-meta">' + pack.stats.map(function(s) {
        return '<div class="ft-stat"><b>' + s.val + '</b><span>' + s.label + '</span></div>';
      }).join("") + '</div>';
    }
 
    var gate = document.createElement("div");
    gate.id = "family-trip-gate-v3";
    gate.className = "family-trip-landing-shell";
    gate.innerHTML =
      '<div class="family-trip-landing-inner">' +
      '<div class="ft-kicker">' + kickerText + '</div>' +
      '<h2 class="ft-title">' + titleText + '</h2>' +
      '<p class="ft-lead">' + leadText + '</p>' +
      routeHtml +
      statsHtml +
      '<div class="ft-unlock">' +
      '<label for="family-trip-pin-v3">Introduceți codul PIN de familie</label>' +
      '<div class="ft-row">' +
      '<input id="family-trip-pin-v3" type="password" inputmode="numeric" autocomplete="off" placeholder="••••" />' +
      '<button id="family-trip-unlock-v3" type="button">Deblochează jurnalul</button>' +
      '</div>' +
      '<div id="family-trip-err-v3" class="ft-err"></div>' +
      '</div>' +
      '<p class="ft-note">Jurnal securizat cu PIN (AES-GCM-256).</p>' +
      '</div>';
 
    var out = document.createElement("div");
    out.id = "family-trip-out-v3";
    out.style.display = "none";
 
    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-ul trebuie să aibă minim 4 cifre."; return; }
      btn.disabled = true;
      err.textContent = "Se deblochează jurnalul…";
      err.style.color = "#1f6aa5";
      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.style.color = "#b32424";
          err.textContent = "Format neașteptat al pachetului.";
          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, pack);
        out.style.display = "block";
        gate.style.display = "none";
      } catch (e) {
        err.style.color = "#b32424";
        err.textContent = "PIN incorect — mai încearcă o dată.";
        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 === */

Latest revision as of 15:54, 30 August 2026

/* 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 injectLandingStyles() {
    if (document.getElementById("family-trip-landing-css")) return;
    var style = document.createElement("style");
    style.id = "family-trip-landing-css";
    style.textContent = [
      "#content.mw-body, #bodyContent { background: transparent !important; }",
      "#firstHeading { display: none !important; }",
      "#siteSub, #contentSub, #jump-to-nav { display: none !important; }",
      ".family-trip-landing-shell {",
      "  position: relative; overflow: hidden; margin: 0 auto 1.5rem; max-width: 720px;",
      "  border-radius: 28px; padding: 2px;",
      "  background: linear-gradient(135deg, rgba(31,106,165,.55), rgba(47,143,107,.45), rgba(240,180,41,.35));",
      "  box-shadow: 0 28px 60px rgba(22,50,74,.18);",
      "}",
      ".family-trip-landing-inner {",
      "  position: relative; border-radius: 26px; overflow: hidden;",
      "  background:",
      "    radial-gradient(1200px 420px at 10% -10%, rgba(255,255,255,.55), transparent 55%),",
      "    radial-gradient(900px 380px at 110% 0%, rgba(240,180,41,.18), transparent 50%),",
      "    linear-gradient(160deg, #f7fbff 0%, #eef6f2 48%, #f8f4ea 100%);",
      "  padding: 34px 28px 30px; font-family: 'Segoe UI', system-ui, sans-serif; color: #16324a;",
      "}",
      ".family-trip-landing-inner::before {",
      "  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .35;",
      "  background-image: linear-gradient(rgba(22,50,74,.035) 1px, transparent 1px),",
      "    linear-gradient(90deg, rgba(22,50,74,.035) 1px, transparent 1px);",
      "  background-size: 28px 28px; mask-image: linear-gradient(180deg, #000, transparent 90%);",
      "}",
      ".ft-kicker { position: relative; display: inline-flex; align-items: center; gap: 8px;",
      "  font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; font-weight: 700;",
      "  color: #1f6aa5; background: rgba(255,255,255,.72); border: 1px solid rgba(31,106,165,.18);",
      "  border-radius: 999px; padding: 7px 12px; margin-bottom: 16px; }",
      ".ft-title { position: relative; margin: 0 0 10px; font-size: clamp(1.8rem, 4vw, 2.45rem);",
      "  line-height: 1.12; font-weight: 750; letter-spacing: -0.02em; color: #12283a; }",
      ".ft-lead { position: relative; margin: 0 0 22px; max-width: 36rem; color: #4d6275;",
      "  font-size: 1.05rem; line-height: 1.55; }",
      ".ft-route { position: relative; display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 22px; }",
      ".ft-chip { display: inline-flex; align-items: center; gap: 6px; border-radius: 999px;",
      "  padding: 8px 12px; background: rgba(255,255,255,.8); border: 1px solid rgba(22,50,74,.08);",
      "  color: #1a3348; font-weight: 650; font-size: .92rem; box-shadow: 0 6px 16px rgba(22,50,74,.05); }",
      ".ft-chip span { opacity: .55; font-weight: 600; }",
      ".ft-meta { position: relative; display: grid; grid-template-columns: repeat(3, minmax(0,1fr));",
      "  gap: 10px; margin: 0 0 24px; }",
      ".ft-stat { background: rgba(255,255,255,.78); border: 1px solid rgba(22,50,74,.07);",
      "  border-radius: 16px; padding: 12px 14px; }",
      ".ft-stat b { display: block; font-size: 1.02rem; color: #143248; margin-bottom: 2px; }",
      ".ft-stat span { color: #62768a; font-size: .86rem; }",
      ".ft-unlock { position: relative; background: rgba(255,255,255,.88);",
      "  border: 1px solid rgba(31,106,165,.14); border-radius: 20px; padding: 18px 18px 16px;",
      "  box-shadow: inset 0 1px 0 rgba(255,255,255,.8), 0 12px 28px rgba(31,106,165,.08); }",
      ".ft-unlock label { display: block; font-weight: 700; color: #184866; margin-bottom: 8px; }",
      ".ft-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }",
      ".ft-row input { flex: 1 1 160px; min-width: 140px; font-size: 1.2rem; letter-spacing: .28em;",
      "  padding: 12px 14px; border-radius: 14px; border: 1px solid #c5d3e0; background: #fff;",
      "  color: #16324a; outline: none; transition: border-color .15s, box-shadow .15s; }",
      ".ft-row input:focus { border-color: #1f6aa5; box-shadow: 0 0 0 4px rgba(31,106,165,.15); }",
      ".ft-row button { appearance: none; border: 0; cursor: pointer; border-radius: 999px;",
      "  padding: 12px 18px; font-weight: 750; color: #fff;",
      "  background: linear-gradient(135deg, #1f6aa5, #2f8f6b);",
      "  box-shadow: 0 10px 22px rgba(31,106,165,.28); transition: transform .12s, filter .12s; }",
      ".ft-row button:hover { filter: brightness(1.05); transform: translateY(-1px); }",
      ".ft-row button:disabled { opacity: .7; cursor: wait; transform: none; }",
      ".ft-err { min-height: 1.25em; margin-top: 10px; color: #b32424; font-size: .95rem; }",
      ".ft-note { position: relative; margin: 16px 0 0; color: #6a7d90; font-size: .88rem; }",
      "@media (max-width: 640px) {",
      "  .family-trip-landing-inner { padding: 26px 18px 22px; }",
      "  .ft-meta { grid-template-columns: 1fr; }",
      "}",
      "#family-trip-out-v3 { margin-top: 1.25rem; }",
      "#family-trip-frame { width: 100%; min-height: 80vh; border: 0; border-radius: 18px;",
      "  box-shadow: 0 18px 48px rgba(15,23,42,.16); background: #fff; }",
      ".trip-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin: 1.5em 0; }",
      ".trip-photo-card { background: #fff; border: 1px solid #d7dee7; border-radius: 16px; overflow: hidden; box-shadow: 0 6px 18px rgba(15,23,42,.06); }",
      ".trip-photo-card img { width: 100%; height: 240px; object-fit: cover; display: block; cursor: pointer; transition: transform .2s ease; }",
      ".trip-photo-card img:hover { transform: scale(1.02); }",
      ".trip-photo-cap { padding: 12px 14px; font-size: .92rem; color: #334155; line-height: 1.4; border-top: 1px solid #f1f5f9; }"
    ].join("\n");
    document.head.appendChild(style);
  }

  function renderHtml(out, html, pack) {
    while (out.firstChild) out.removeChild(out.firstChild);
    var frame = document.createElement("iframe");
    frame.id = "family-trip-frame";
    var tripTitle = (pack && pack.title) ? pack.title : "Family Trip Journal";
    var tripSubtitle = (pack && pack.heroSubtitle) ? pack.heroSubtitle : "Family road journal · PIN unlocked";
    frame.title = tripTitle;
    frame.setAttribute("sandbox", "allow-same-origin allow-popups allow-popups-to-escape-sandbox");

    var heroHeader = (pack && pack.noHeroHeader) ? "" : '<div class="hero"><h1>' + tripTitle + '</h1><p>' + tripSubtitle + '</p></div>';

    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;}' +
      '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;}' +
      '.trip-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin: 1.5em 0; }' +
      '.trip-photo-card { background: #fff; border: 1px solid #d7dee7; border-radius: 16px; overflow: hidden; box-shadow: 0 6px 18px rgba(15,23,42,.06); }' +
      '.trip-photo-card img { width: 100%; height: 260px; object-fit: cover; display: block; border-radius: 16px 16px 0 0; }' +
      '.trip-photo-cap { padding: 12px 14px; font-size: .92rem; color: #334155; line-height: 1.4; border-top: 1px solid #f1f5f9; }' +
      '.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">' +
      heroHeader +
      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");
    injectLandingStyles();

    var pack;
    try { pack = JSON.parse(el.textContent); } catch (e) { return; }

    var fallback = document.getElementById("family-trip-landing-fallback");
    if (fallback) fallback.style.display = "none";

    var kickerText = (pack && pack.kicker) ? pack.kicker : "Jurnal de vacanță · Criptat";
    var titleText = (pack && pack.title) ? pack.title : "Jurnal de Vacanță";
    var leadText = (pack && pack.lead) ? pack.lead : "Introduceți codul PIN pentru a deschide jurnalul de călătorie și galeria foto.";

    var routeHtml = "";
    if (pack && pack.chips && Array.isArray(pack.chips) && pack.chips.length > 0) {
      routeHtml = '<div class="ft-route">' + pack.chips.map(function(c) {
        return '<div class="ft-chip">' + c + '</div>';
      }).join("") + '</div>';
    }

    var statsHtml = "";
    if (pack && pack.stats && Array.isArray(pack.stats) && pack.stats.length > 0) {
      statsHtml = '<div class="ft-meta">' + pack.stats.map(function(s) {
        return '<div class="ft-stat"><b>' + s.val + '</b><span>' + s.label + '</span></div>';
      }).join("") + '</div>';
    }

    var gate = document.createElement("div");
    gate.id = "family-trip-gate-v3";
    gate.className = "family-trip-landing-shell";
    gate.innerHTML =
      '<div class="family-trip-landing-inner">' +
      '<div class="ft-kicker">' + kickerText + '</div>' +
      '<h2 class="ft-title">' + titleText + '</h2>' +
      '<p class="ft-lead">' + leadText + '</p>' +
      routeHtml +
      statsHtml +
      '<div class="ft-unlock">' +
      '<label for="family-trip-pin-v3">Introduceți codul PIN de familie</label>' +
      '<div class="ft-row">' +
      '<input id="family-trip-pin-v3" type="password" inputmode="numeric" autocomplete="off" placeholder="••••" />' +
      '<button id="family-trip-unlock-v3" type="button">Deblochează jurnalul</button>' +
      '</div>' +
      '<div id="family-trip-err-v3" class="ft-err"></div>' +
      '</div>' +
      '<p class="ft-note">Jurnal securizat cu PIN (AES-GCM-256).</p>' +
      '</div>';

    var out = document.createElement("div");
    out.id = "family-trip-out-v3";
    out.style.display = "none";

    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-ul trebuie să aibă minim 4 cifre."; return; }
      btn.disabled = true;
      err.textContent = "Se deblochează jurnalul…";
      err.style.color = "#1f6aa5";
      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.style.color = "#b32424";
          err.textContent = "Format neașteptat al pachetului.";
          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, pack);
        out.style.display = "block";
        gate.style.display = "none";
      } catch (e) {
        err.style.color = "#b32424";
        err.textContent = "PIN incorect — mai încearcă o dată.";
        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 === */