Pekora Reborn Script

Part 2 of Pekora Reborn

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Pekora Reborn Script
// @namespace    pekora reborn
// @version      1.0
// @author       1547
// @description  Part 2 of Pekora Reborn
// @match        https://pekora.zip/*
// @match        https://www.pekora.zip/*
// @run-at       document-start
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  console.log("loading PekoraReborn", location.href);

  const OLD_RE = /Korone/gi;
  const NEW = "Pekora";
  const FAVICON = "https://web.archive.org/web/20250826011158if_/https://www.pekora.zip/favicon.ico";

 // allowed
const allowedSelectors = [
  "title",
  ".navbar", ".navbar-brand", "header", "nav", ".topbar", ".menu", ".nav",
  ".downloadText-0-2-269", ".download", ".card-0-2-268", ".image-0-2-267", ".subTitle-0-2-266",
  ".profile", ".account", ".form-group", "label",
  "h1", "h3", "h5", "p", "button", ".btn", ".pageTitle-0-2-388", ".subTitle-0-2-590",
  ".modalHeaderText-0-2-340", ".modalBody-0-2-341", ".modalContent-0-2-337", ".modalDialog-0-2-336",
  ".newBuyButton-0-2-87", ".button2-0-2-358",

  // allow footer
  ".footerNote-0-2-36",
  ".footerNote-d2-0-2-39",
  ".footerLink-0-2-359"
];



  // blocked
  const blockedSelectors = [
    ".userStatus", ".user-status", ".status", ".profile-status",
    ".userStatus-0-2-195", // User status (specific Korone class)
    ".alertBg-0-2-25",     // Emergency banner (specific Korone class)
    ".body-0-2-141", ".about", ".profile-about",
    ".gameCard", ".gameCardTitle", ".gameCardContainer-0-2-140",
    ".item", ".inventory", ".catalog", ".store",
    ".alert", ".alertText", ".alertLink", ".banner",
    ".chat", ".message", ".messages"
  ];



  function ensureFavicon() {
    try {
      const setHref = (link) => {
        if (!link) return;
        if (link.getAttribute('href') !== FAVICON) link.setAttribute('href', FAVICON);
      };
      let link = document.querySelector('link[rel="icon"]') || document.querySelector('link[href$=".ico"]');
      if (!link) {
        link = document.createElement('link'); link.rel = 'icon'; document.head.appendChild(link);
      }
      setHref(link);
      let shortcut = document.querySelector('link[rel="shortcut icon"]');
      if (!shortcut) {
        shortcut = document.createElement('link'); shortcut.rel = 'shortcut icon'; document.head.appendChild(shortcut);
      }
      setHref(shortcut);
    } catch (e) { console.warn("[PekoraReborn] favicon err", e); }
  }

  function nodeInsideAny(node, selectors) {
    if (!node || !node.parentElement) return false;
    return selectors.some(sel => {
      try { return node.parentElement.closest(sel); } catch (e) { return false; }
    });
  }

  // decides whether a TextNode should be replaced
  function canReplace(textNode) {
    if (!textNode || textNode.nodeType !== Node.TEXT_NODE) return false;
    const txt = textNode.nodeValue;
    if (!txt || !OLD_RE.test(txt)) return false;

    // if its in a prohibited block = dont allow
    if (nodeInsideAny(textNode, blockedSelectors)) return false;

    // if its allowed = allow
    if (nodeInsideAny(textNode, allowedSelectors)) return true;

    // detect loading or button messages with Korone
if (/Korone is now loading|Download and Install Korone|Featured Items on Korone/i.test(txt)) {
  return true;
}


    // if are on .card-body = only allow if are in /download or /setup
    const cardBodyAncestor = textNode.parentElement && textNode.parentElement.closest('.card-body, .card-0-2-268');
    if (cardBodyAncestor) {
      const p = location.pathname || '';
      if (p.includes('/download') || p.includes('/setup')) return true;
      return false;
    }
   // allow change copyright text
  // prevents breaking other rules, but ensures that Footer always changes
  if (/^©/.test(txt.trim())) {
  return true;
}

    // dont replace (for defect)
    return false;
  }

  function replacePreserveCase(str) {
    return str.replace(/Korone|KORONE|korone/gi, match => {
      if (match === match.toUpperCase()) return NEW.toUpperCase();
      if (match[0] === match[0].toUpperCase()) return NEW;
      return NEW.toLowerCase();
    });
  }

  function processTextNode(tn) {
    try {
      if (canReplace(tn)) {
        const original = tn.nodeValue;
        const replaced = replacePreserveCase(original);
        if (replaced !== original) tn.nodeValue = replaced;
      }
    } catch (e) { /* no romper página */ }
  }

  function scanRoot(root) {
    if (!root) return 0;
    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
    let count = 0;
    let n;
    while ((n = walker.nextNode())) {
      processTextNode(n);
      count++;
    }
    return count;
  }

  function applyAll() {
    try {
      // Prioritize header/nav/footer
      ['header', 'nav', '.navbar', '.topbar', '.menu', '.footer'].forEach(sel => {
        const el = document.querySelector(sel);
        if (el) scanRoot(el);
      });
      // scan downloads/account containers specifically
      const downloadRoot = document.querySelector('.download, .downloadText-0-2-269, .card-0-2-268');
      if (downloadRoot) scanRoot(downloadRoot);
      const accountRoot = document.querySelector('.profile, .account, .form-group');
      if (accountRoot) scanRoot(accountRoot);

      // fallback: scan body but our canReplace prevents changing content
      scanRoot(document.body);

      // title & favicon
      if (OLD_RE.test(document.title)) document.title = replacePreserveCase(document.title);
      ensureFavicon();
    } catch (e) { console.warn("PekoraReborn applyAll err", e); }
  }

  // Observer: process added nodes and characterData
  const mo = new MutationObserver((mutations) => {
    for (const m of mutations) {
      if (m.type === 'characterData' && m.target && m.target.nodeType === Node.TEXT_NODE) {
        processTextNode(m.target);
      }
      if (m.addedNodes && m.addedNodes.length) {
        m.addedNodes.forEach(node => {
          if (node.nodeType === Node.TEXT_NODE) processTextNode(node);
          else if (node.nodeType === Node.ELEMENT_NODE) scanRoot(node);
        });
      }
    }
  });

  function hookHistory() {
    const wrap = (orig) => function() {
      const res = orig.apply(this, arguments);
      setTimeout(applyAll, 120);
      return res;
    };
    history.pushState = wrap(history.pushState);
    history.replaceState = wrap(history.replaceState);
    window.addEventListener('popstate', () => setTimeout(applyAll, 120));
  }

  // Safe init
  function init() {
    console.log("PekoraReborn init");
    try {
      applyAll();
      try { mo.observe(document.documentElement || document.body, { childList: true, subtree: true, characterData: true }); } catch(e) { mo.observe(document.body, { childList: true, subtree: true, characterData: true }); }
      hookHistory();
      // fallback: run again a little later if something prevents observer
      setTimeout(applyAll, 800);
      // extra safety: if VM didn't get DOMContentLoaded, run periodically a couple times
      let tries = 0;
      const t = setInterval(() => { if (tries++ > 5) return clearInterval(t); applyAll(); }, 1000);
    } catch(e) { console.error("[PekoraReborn] init error", e); }
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
    // also try early
    setTimeout(() => { if (document.readyState !== 'complete') init(); }, 200);
  } else init();

})();