User:CannonProductions/monacobookbeta.js: Difference between revisions

From MediaWiki
Jump to navigationJump to search
No edit summary
No edit summary
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* User:CannonProductions/monacobookbeta.js
/* User:CannonProductions/monacobookbeta.js
   - Dynamically scales #top-ad iframe banner to fit available width on mobile
   - Scales #top-ad iframe banner to fit available width on mobile
   - Locks background scroll when the sidebar (#column-one) is open on mobile
   - Locks background scroll when #column-one is open on mobile
  - Adds html.tf-nav-open while the sidebar is open
  - Aggressively removes the redundant mobile logo image block (p-m-logo / mobilelogo.jpg)
*/
*/


Line 8: Line 10:


   // === SETTINGS ===
   // === SETTINGS ===
   var DEBUG_BADGE = false;         // keep false (you already confirmed it runs)
   var DEBUG_BADGE = false;
   var MOBILE_MAX_WIDTH = 720;     // rules apply on screens <= this width
   var MOBILE_MAX_WIDTH = 720;


   // Banner creative’s native size
   // Banner creative’s native size
Line 45: Line 47:
     return window.matchMedia("(max-width: " + MOBILE_MAX_WIDTH + "px)").matches;
     return window.matchMedia("(max-width: " + MOBILE_MAX_WIDTH + "px)").matches;
   }
   }
  // ===== Kill redundant mobile logo as hard as possible =====
  function nukeMobileLogo() {
    try {
      // 1) Remove the whole portlet if present
      var p = document.getElementById("p-m-logo");
      if (p && p.parentNode) p.parentNode.removeChild(p);
      // 2) If it gets reinserted or appears elsewhere, kill any image that looks like it
      var imgs = document.querySelectorAll('img[src*="mobilelogo"]');
      for (var i = 0; i < imgs.length; i++) {
        // try to stop load ASAP
        imgs[i].src = "";
        // remove a wrapper link if it exists, otherwise just remove img
        var wrap = imgs[i].closest ? imgs[i].closest("#p-m-logo, a, div") : null;
        if (wrap && wrap.parentNode) wrap.parentNode.removeChild(wrap);
        else if (imgs[i].parentNode) imgs[i].parentNode.removeChild(imgs[i]);
      }
    } catch (e) {
      // swallow — we don't want a single hiccup to break everything else
    }
  }
  // Run it as early as we can
  nukeMobileLogo();
  document.addEventListener("readystatechange", function () {
    if (document.readyState === "interactive" || document.readyState === "complete") {
      nukeMobileLogo();
    }
  });


   // ===== Banner scaling =====
   // ===== Banner scaling =====
Line 94: Line 126:
   }
   }


   // ===== Nav scroll locking =====
   // ===== Nav open detection =====
   // MonacoBookBeta toggles #column-one visibility; when it’s visible on mobile, stop the page behind from scrolling.
   function isNavOpen() {
    var nav = document.getElementById("column-one");
    if (!nav) return false;
 
    var cs = window.getComputedStyle(nav);
    if (!cs) return false;
    if (cs.display === "none") return false;
 
    var r = nav.getBoundingClientRect();
    if (r.width < 20 || r.height < 20) return false;
 
    // must intersect viewport
    if (r.right <= 0 || r.bottom <= 0) return false;
    if (r.left >= window.innerWidth || r.top >= window.innerHeight) return false;
 
    return true;
  }
 
  // ===== Nav scroll locking + html class =====
   function setScrollLocked(locked) {
   function setScrollLocked(locked) {
    // Use a data flag so we don’t fight other styles
     if (locked) {
     if (locked) {
       document.documentElement.style.overflow = "hidden";
       document.documentElement.style.overflow = "hidden";
Line 111: Line 160:
   }
   }


   function isNavOpen() {
   function updateNavLockAndClass() {
    var nav = document.getElementById("column-one");
    if (!nav) return false;
 
    var cs = window.getComputedStyle(nav);
    if (!cs || cs.display === "none" || cs.visibility === "hidden") return false;
 
    var r = nav.getBoundingClientRect();
    // If it has some real onscreen area, treat it as open
    return r.width > 40 && r.height > 40;
  }
 
  function updateNavLock() {
     if (!isMobileWidth()) {
     if (!isMobileWidth()) {
       setScrollLocked(false);
       setScrollLocked(false);
      document.documentElement.classList.remove("tf-nav-open");
       return;
       return;
     }
     }
     setScrollLocked(isNavOpen());
 
    var open = isNavOpen();
     setScrollLocked(open);
 
    if (open) document.documentElement.classList.add("tf-nav-open");
    else document.documentElement.classList.remove("tf-nav-open");
   }
   }


Line 136: Line 179:
     if (t) clearTimeout(t);
     if (t) clearTimeout(t);
     t = setTimeout(function () {
     t = setTimeout(function () {
      nukeMobileLogo();
       rescaleBanner();
       rescaleBanner();
       updateNavLock();
       updateNavLockAndClass();
     }, 60);
     }, 60);
   }
   }


   function installObservers() {
   function installObservers() {
     // Run once now
     nukeMobileLogo();
     rescaleBanner();
     rescaleBanner();
     updateNavLock();
     updateNavLockAndClass();


    // Re-run on resize/rotate
     window.addEventListener("resize", scheduleAll);
     window.addEventListener("resize", scheduleAll);
     window.addEventListener("orientationchange", scheduleAll);
     window.addEventListener("orientationchange", scheduleAll);


    // MediaWiki dynamic content hook
     if (window.mw && mw.hook) {
     if (window.mw && mw.hook) {
       mw.hook("wikipage.content").add(function () {
       mw.hook("wikipage.content").add(function () {
Line 157: Line 199:
     }
     }


     // Watch for DOM changes (menu open/close often changes styles/DOM)
     // If the skin re-inserts the logo, kill it again
     var obs = new MutationObserver(function () {
     var obs = new MutationObserver(function () {
       scheduleAll();
       scheduleAll();
Line 163: Line 205:
     obs.observe(document.documentElement, { childList: true, subtree: true, attributes: true });
     obs.observe(document.documentElement, { childList: true, subtree: true, attributes: true });


    // Extra safety: click anywhere updates lock (covers purely-CSS toggles)
     document.addEventListener("click", function () {
     document.addEventListener("click", function () {
       setTimeout(updateNavLock, 0);
       setTimeout(updateNavLockAndClass, 0);
     });
     });
   }
   }

Latest revision as of 13:07, 30 December 2025

/* User:CannonProductions/monacobookbeta.js
   - Scales #top-ad iframe banner to fit available width on mobile
   - Locks background scroll when #column-one is open on mobile
   - Adds html.tf-nav-open while the sidebar is open
   - Aggressively removes the redundant mobile logo image block (p-m-logo / mobilelogo.jpg)
*/

(function () {
  "use strict";

  // === SETTINGS ===
  var DEBUG_BADGE = false;
  var MOBILE_MAX_WIDTH = 720;

  // Banner creative’s native size
  var BASE_W = 468;
  var BASE_H = 60;

  // Banner scale limits
  var MIN_SCALE = 0.55;
  var MAX_SCALE = 1.0;

  function onReady(fn) {
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", fn);
    } else {
      fn();
    }
  }

  function addBadge() {
    if (!DEBUG_BADGE) return;
    if (!document.body) return;
    if (document.getElementById("js-test-badge")) return;

    var d = document.createElement("div");
    d.id = "js-test-badge";
    d.textContent = "User JS RUNNING";
    d.style.cssText =
      "position:fixed;top:0;left:0;z-index:999999;" +
      "padding:6px 10px;background:#e11;color:#fff;" +
      "font:12px sans-serif";
    document.body.prepend(d);
  }

  function isMobileWidth() {
    return window.matchMedia("(max-width: " + MOBILE_MAX_WIDTH + "px)").matches;
  }

  // ===== Kill redundant mobile logo as hard as possible =====
  function nukeMobileLogo() {
    try {
      // 1) Remove the whole portlet if present
      var p = document.getElementById("p-m-logo");
      if (p && p.parentNode) p.parentNode.removeChild(p);

      // 2) If it gets reinserted or appears elsewhere, kill any image that looks like it
      var imgs = document.querySelectorAll('img[src*="mobilelogo"]');
      for (var i = 0; i < imgs.length; i++) {
        // try to stop load ASAP
        imgs[i].src = "";
        // remove a wrapper link if it exists, otherwise just remove img
        var wrap = imgs[i].closest ? imgs[i].closest("#p-m-logo, a, div") : null;
        if (wrap && wrap.parentNode) wrap.parentNode.removeChild(wrap);
        else if (imgs[i].parentNode) imgs[i].parentNode.removeChild(imgs[i]);
      }
    } catch (e) {
      // swallow — we don't want a single hiccup to break everything else
    }
  }

  // Run it as early as we can
  nukeMobileLogo();
  document.addEventListener("readystatechange", function () {
    if (document.readyState === "interactive" || document.readyState === "complete") {
      nukeMobileLogo();
    }
  });

  // ===== Banner scaling =====
  function resetBannerStyles(ad, iframe) {
    if (ad) {
      ad.style.height = "";
      ad.style.overflow = "";
      ad.style.width = "";
    }
    if (iframe) {
      iframe.style.transform = "";
      iframe.style.transformOrigin = "";
      iframe.style.width = "";
      iframe.style.height = "";
      iframe.style.border = "";
      iframe.style.display = "";
    }
  }

  function rescaleBanner() {
    var ad = document.getElementById("top-ad");
    if (!ad) return;

    var iframe = ad.querySelector("iframe");
    if (!iframe) return;

    if (!isMobileWidth()) {
      resetBannerStyles(ad, iframe);
      return;
    }

    iframe.style.width = BASE_W + "px";
    iframe.style.height = BASE_H + "px";
    iframe.style.border = "0";
    iframe.style.display = "block";
    iframe.style.transformOrigin = "top left";

    var available = ad.getBoundingClientRect().width || window.innerWidth;

    var scale = available / BASE_W;
    if (scale > MAX_SCALE) scale = MAX_SCALE;
    if (scale < MIN_SCALE) scale = MIN_SCALE;

    iframe.style.transform = "scale(" + scale.toFixed(4) + ")";

    ad.style.width = "100%";
    ad.style.overflow = "hidden";
    ad.style.height = (BASE_H * scale) + "px";
  }

  // ===== Nav open detection =====
  function isNavOpen() {
    var nav = document.getElementById("column-one");
    if (!nav) return false;

    var cs = window.getComputedStyle(nav);
    if (!cs) return false;
    if (cs.display === "none") return false;

    var r = nav.getBoundingClientRect();
    if (r.width < 20 || r.height < 20) return false;

    // must intersect viewport
    if (r.right <= 0 || r.bottom <= 0) return false;
    if (r.left >= window.innerWidth || r.top >= window.innerHeight) return false;

    return true;
  }

  // ===== Nav scroll locking + html class =====
  function setScrollLocked(locked) {
    if (locked) {
      document.documentElement.style.overflow = "hidden";
      document.body.style.overflow = "hidden";
      document.body.setAttribute("data-nav-locked", "1");
    } else {
      if (document.body.getAttribute("data-nav-locked") === "1") {
        document.documentElement.style.overflow = "";
        document.body.style.overflow = "";
        document.body.removeAttribute("data-nav-locked");
      }
    }
  }

  function updateNavLockAndClass() {
    if (!isMobileWidth()) {
      setScrollLocked(false);
      document.documentElement.classList.remove("tf-nav-open");
      return;
    }

    var open = isNavOpen();
    setScrollLocked(open);

    if (open) document.documentElement.classList.add("tf-nav-open");
    else document.documentElement.classList.remove("tf-nav-open");
  }

  // ===== Plumbing =====
  var t = null;
  function scheduleAll() {
    if (t) clearTimeout(t);
    t = setTimeout(function () {
      nukeMobileLogo();
      rescaleBanner();
      updateNavLockAndClass();
    }, 60);
  }

  function installObservers() {
    nukeMobileLogo();
    rescaleBanner();
    updateNavLockAndClass();

    window.addEventListener("resize", scheduleAll);
    window.addEventListener("orientationchange", scheduleAll);

    if (window.mw && mw.hook) {
      mw.hook("wikipage.content").add(function () {
        scheduleAll();
      });
    }

    // If the skin re-inserts the logo, kill it again
    var obs = new MutationObserver(function () {
      scheduleAll();
    });
    obs.observe(document.documentElement, { childList: true, subtree: true, attributes: true });

    document.addEventListener("click", function () {
      setTimeout(updateNavLockAndClass, 0);
    });
  }

  onReady(function () {
    addBadge();
    installObservers();
  });
})();