MediaWiki:Common.js
Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.
- Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
- Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
- Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
- Opera: Нажмите Ctrl+F5.
/* MediaWiki:Common.js — Orbitalis Wiki
* This code runs on every wiki page.
* Paste this into MediaWiki:Common.js on the wiki (requires admin rights).
*
* Parallax space background that follows the mouse cursor,
* matching the in-game lobby style.
*/
(function () {
"use strict";
// Only activate on the main page (or everywhere — your choice)
var parallaxBg = document.getElementById("orbitalis-parallax-bg");
if (!parallaxBg) return; // no parallax container on this page
var nebulaEl = document.getElementById("parallax-nebula");
var starsSmallEl = document.getElementById("parallax-stars-small");
var starsBigEl = document.getElementById("parallax-stars-big");
// Disable parallax on diff/comparison and old revision pages
var params = new URLSearchParams(window.location.search);
if (
params.has("diff") ||
params.has("oldid") ||
params.get("action") === "historysubmit"
) {
var hideEls = [
parallaxBg,
nebulaEl,
starsSmallEl,
starsBigEl,
document.getElementById("orbitalis-vignette"),
];
for (var h = 0; h < hideEls.length; h++) {
if (hideEls[h]) hideEls[h].style.display = "none";
}
// Restore opaque background so diff/old revision is readable
document.body.style.background = "#060608";
var contentEl = document.getElementById("content");
if (contentEl) {
contentEl.style.background = "#14141a";
}
return;
}
// ---- Apply styles to the parallax elements ----------------------------
// We do it from JS because MediaWiki strips complex inline styles
// Background container
parallaxBg.style.cssText =
"position:fixed;top:0;left:0;width:100%;height:100%;z-index:-10;pointer-events:none;overflow:hidden;background:#060608;";
// Shared layer base
var layerBase =
"position:fixed;top:-50px;left:-50px;width:calc(100% + 100px);height:calc(100% + 100px);pointer-events:none;";
// Nebula
if (nebulaEl) {
nebulaEl.style.cssText =
layerBase +
"background:" +
"radial-gradient(ellipse at 70% 40%, rgba(30,50,80,0.4) 0%, transparent 50%)," +
"radial-gradient(ellipse at 85% 60%, rgba(50,30,60,0.3) 0%, transparent 45%)," +
"radial-gradient(ellipse at 60% 70%, rgba(20,40,70,0.35) 0%, transparent 55%);" +
"opacity:0.7;z-index:-9;";
}
// Small stars
if (starsSmallEl) {
starsSmallEl.style.cssText =
layerBase +
"background-image:" +
"radial-gradient(1px 1px at 10% 20%, rgba(255,255,255,0.8), transparent)," +
"radial-gradient(1px 1px at 25% 35%, rgba(255,255,255,0.6), transparent)," +
"radial-gradient(1px 1px at 40% 10%, rgba(255,255,255,0.7), transparent)," +
"radial-gradient(1px 1px at 55% 45%, rgba(255,255,255,0.5), transparent)," +
"radial-gradient(1px 1px at 70% 25%, rgba(255,255,255,0.8), transparent)," +
"radial-gradient(1px 1px at 85% 55%, rgba(255,255,255,0.6), transparent)," +
"radial-gradient(1px 1px at 15% 60%, rgba(255,255,255,0.7), transparent)," +
"radial-gradient(1px 1px at 30% 75%, rgba(255,255,255,0.5), transparent)," +
"radial-gradient(1px 1px at 45% 85%, rgba(255,255,255,0.8), transparent)," +
"radial-gradient(1px 1px at 60% 70%, rgba(255,255,255,0.6), transparent)," +
"radial-gradient(1px 1px at 75% 90%, rgba(255,255,255,0.7), transparent)," +
"radial-gradient(1px 1px at 90% 15%, rgba(255,255,255,0.5), transparent)," +
"radial-gradient(1px 1px at 5% 40%, rgba(255,255,255,0.6), transparent)," +
"radial-gradient(1px 1px at 20% 95%, rgba(255,255,255,0.7), transparent)," +
"radial-gradient(1px 1px at 35% 50%, rgba(255,255,255,0.5), transparent)," +
"radial-gradient(1px 1px at 50% 30%, rgba(255,255,255,0.8), transparent)," +
"radial-gradient(1px 1px at 65% 5%, rgba(255,255,255,0.6), transparent)," +
"radial-gradient(1px 1px at 80% 65%, rgba(255,255,255,0.7), transparent)," +
"radial-gradient(1px 1px at 95% 80%, rgba(255,255,255,0.5), transparent)," +
"radial-gradient(1px 1px at 12% 88%, rgba(255,255,255,0.6), transparent);" +
"background-size:200px 200px;opacity:0.8;z-index:-8;";
}
// Big stars
if (starsBigEl) {
starsBigEl.style.cssText =
layerBase +
"background-image:" +
"radial-gradient(2px 2px at 8% 15%, rgba(200,220,255,0.9), transparent)," +
"radial-gradient(2px 2px at 22% 42%, rgba(255,240,220,0.8), transparent)," +
"radial-gradient(2px 2px at 38% 8%, rgba(220,255,255,0.7), transparent)," +
"radial-gradient(2px 2px at 52% 68%, rgba(255,255,220,0.8), transparent)," +
"radial-gradient(2px 2px at 68% 32%, rgba(200,200,255,0.9), transparent)," +
"radial-gradient(2px 2px at 82% 78%, rgba(255,220,200,0.7), transparent)," +
"radial-gradient(2px 2px at 18% 55%, rgba(220,255,220,0.8), transparent)," +
"radial-gradient(2px 2px at 48% 92%, rgba(255,200,255,0.7), transparent)," +
"radial-gradient(2px 2px at 72% 18%, rgba(200,255,255,0.8), transparent)," +
"radial-gradient(2px 2px at 92% 45%, rgba(255,255,200,0.7), transparent);" +
"background-size:300px 300px;opacity:0.6;z-index:-7;";
}
// Vignette overlay
var vignetteEl = document.getElementById("orbitalis-vignette");
if (vignetteEl) {
vignetteEl.style.cssText =
"position:fixed;top:0;left:0;width:100%;height:100%;z-index:-6;pointer-events:none;" +
"background:radial-gradient(ellipse at center, rgba(6,6,8,0) 30%, rgba(6,6,8,0.6) 70%, #060608 100%);";
}
// ---- Override MediaWiki default backgrounds ----------------------------
document.body.style.background = "transparent";
var ids = ["mw-page-base", "mw-head-base"];
for (var i = 0; i < ids.length; i++) {
var el = document.getElementById(ids[i]);
if (el) el.style.background = "none";
}
var content = document.getElementById("content");
if (content) {
content.style.background = "none";
content.style.border = "none";
}
var bodyContent = document.getElementById("bodyContent");
if (bodyContent) {
bodyContent.style.position = "relative";
}
// ---- Mouse-tracking parallax animation --------------------------------
var targetX = 0,
targetY = 0;
var currentX = 0,
currentY = 0;
document.addEventListener("mousemove", function (e) {
var w = window.innerWidth || document.documentElement.clientWidth;
var h = window.innerHeight || document.documentElement.clientHeight;
targetX = (e.clientX / w - 0.5) * 2;
targetY = (e.clientY / h - 0.5) * 2;
});
function animate() {
currentX += (targetX - currentX) * 0.06;
currentY += (targetY - currentY) * 0.06;
if (nebulaEl) {
nebulaEl.style.left = currentX * 12 - 50 + "px";
nebulaEl.style.top = currentY * 12 - 50 + "px";
}
if (starsSmallEl) {
starsSmallEl.style.left = currentX * 25 - 50 + "px";
starsSmallEl.style.top = currentY * 25 - 50 + "px";
}
if (starsBigEl) {
starsBigEl.style.left = currentX * 40 - 50 + "px";
starsBigEl.style.top = currentY * 40 - 50 + "px";
}
requestAnimationFrame(animate);
}
animate();
})();
/* ---- Server online status + info ---- */
/* Queries /api/status.php on the same wiki host — which sends a UDP BYOND
* topic to localhost:41060 and returns JSON { players, map_name, chaos_level, ... }.
* No proxy needed: wiki and game server share the same machine.
*/
(function () {
"use strict";
var elOnline = document.getElementById("orb-online");
var elMap = document.getElementById("orb-map-val");
var elST = document.getElementById("orb-st-val");
if (!elOnline) return;
var STATUS_URL = "/api/status.json";
var REFRESH_MS = 30000;
function update() {
fetch(STATUS_URL)
.then(function (r) {
console.log("[Orbitalis Status] HTTP", r.status, r.url);
if (!r.ok) throw new Error("HTTP " + r.status);
return r.json();
})
.then(function (data) {
console.log("[Orbitalis Status] Data:", data);
var count = parseInt(data.players, 10);
if (isNaN(count)) throw new Error("bad data: players=" + data.players);
elOnline.textContent = count + " онлайн";
elOnline.style.color =
count > 0 ? "rgba(100,220,180,0.75)" : "rgba(100,220,180,0.35)";
elOnline.style.textShadow =
count > 0 ? "0 0 15px rgba(100,220,180,0.2)" : "none";
if (elMap && data.map_name) {
elMap.textContent = data.map_name;
elMap.style.color = "rgba(255,255,255,0.55)";
}
if (elST && data.chaos_level) {
elST.textContent = data.chaos_level;
elST.style.color = "rgba(255,255,255,0.55)";
}
})
.catch(function (err) {
console.error("[Orbitalis Status] Fetch failed:", err);
elOnline.textContent = "оффлайн";
elOnline.style.color = "rgba(255,120,120,0.5)";
elOnline.style.textShadow = "none";
if (elMap) elMap.textContent = "—";
if (elST) elST.textContent = "—";
});
}
update();
setInterval(update, REFRESH_MS);
})();
/* ---- Main page: interactive engine ---- */
/* All visual styling is in Common.css (CSS-first approach).
JS handles: nuclear strip for <a> tags, click handlers.
Safe to re-run on Citizen DOM updates. */
(function () {
"use strict";
function run() {
var orb = document.getElementById("orb-content");
if (!orb) return;
/* Nuclear strip: clear Citizen-applied backgrounds on <a> tags only */
var links = orb.querySelectorAll("a");
for (var i = 0; i < links.length; i++) {
var a = links[i];
a.style.setProperty("background", "none", "important");
a.style.setProperty("background-color", "transparent", "important");
a.style.setProperty("background-image", "none", "important");
a.style.setProperty("box-shadow", "none", "important");
a.style.setProperty("border", "none", "important");
a.style.setProperty("padding", "0", "important");
a.style.setProperty("text-decoration", "none", "important");
}
/* Connect button — byond:// link */
var addr = document.getElementById("orb-addr");
if (addr && !addr._orbClick) {
addr._orbClick = true;
addr.addEventListener("click", function () {
window.location.href = "byond://45.141.208.222:41060";
});
}
/* Make entire card area clickable */
var cardIds = [
"orb-card-1",
"orb-card-2",
"orb-card-3",
"orb-card-4",
"orb-card-5",
"orb-card-6",
"orb-card-7",
"orb-card-8",
];
for (var j = 0; j < cardIds.length; j++) {
var card = document.getElementById(cardIds[j]);
if (card && !card._orbClick) {
card._orbClick = true;
(function (c) {
var link = c.querySelector("a");
if (link) {
c.addEventListener("click", function (ev) {
if (
ev.target.tagName !== "A" &&
(!ev.target.parentNode || ev.target.parentNode.tagName !== "A")
) {
link.click();
}
});
}
})(card);
}
}
}
/* Hook into multiple events — retry for Citizen's late DOM updates */
if (typeof mw !== "undefined" && mw.hook) {
mw.hook("wikipage.content").add(run);
}
document.addEventListener("DOMContentLoaded", run);
window.addEventListener("load", function () {
setTimeout(run, 50);
setTimeout(run, 500);
});
})();
// Переключение вкладок и классов "chaos-*".
mw.hook("wikipage.content").add(function ($root) {
$root.find(".hj-chaos-container").each(function () {
var $container = $(this);
var $buttons = $container.find(".hj-chaos-tab-button");
var $blocks = $container.find(".hj-chaos-block");
if (!$buttons.length || !$blocks.length) return;
var CHAOS_CLASSES = "chaos-overview chaos-calm chaos-medium chaos-high";
function activate(key, $btn) {
// активная кнопка
$buttons.removeClass("active");
$btn.addClass("active");
// активный блок
$blocks.removeClass("active").hide();
$blocks
.filter('[data-chaos="' + key + '"]')
.addClass("active")
.show();
// класс на контейнере для раскраски рамок/фона
$container.removeClass(CHAOS_CLASSES).addClass("chaos-" + key);
}
// Инициализация
var $activeBtn = $buttons.filter(".active").first();
if ($activeBtn.length) {
activate($activeBtn.data("chaos"), $activeBtn);
} else {
var $first = $buttons.first();
activate($first.data("chaos"), $first);
}
// Клики
$buttons.off("click.hjChaos").on("click.hjChaos", function () {
var $btn = $(this);
activate($btn.data("chaos"), $btn);
});
});
});
/* ---- Orbitalis custom TOC ---- */
/* Activates when the page contains <div id="orb-toc"></div> (or class="orb-toc").
Creates #orb-toc-sidebar directly in document.body so position:fixed
is never broken by skin containers with transform/will-change. */
(function () {
"use strict";
function initOrbToc() {
// Already built?
if (document.getElementById("orb-toc-sidebar")) return;
// Don't show in editor or other non-view actions
if (document.body.classList.contains("action-edit") ||
document.body.classList.contains("action-submit") ||
document.body.classList.contains("action-history")) return;
// Look for the opt-in marker
var marker =
document.getElementById("orb-toc") || document.querySelector(".orb-toc");
if (!marker) return;
// Find content root
var root =
document.querySelector(".mw-parser-output") ||
document.getElementById("mw-content-text") ||
document.getElementById("bodyContent");
if (!root) return;
// Scan ALL heading levels (h1 through h4)
var headings = root.querySelectorAll("h1, h2, h3, h4");
if (!headings.length) return;
var items = [];
for (var i = 0; i < headings.length; i++) {
var h = headings[i];
// Citizen skin: heading text in .mw-headline span
var headline = h.querySelector(".mw-headline");
var text = headline
? headline.textContent
: h.textContent
.replace(/\[\s*править[^\]]*\]/gi, "")
.replace(/\[edit[^\]]*\]/gi, "")
.trim();
if (!text) continue;
var id = headline ? headline.id : h.id;
if (!id) {
id = "orb-h-" + i;
if (headline) headline.id = id;
else h.id = id;
}
var tag = h.tagName.toLowerCase();
items.push({ id: id, text: text, tag: tag, el: h });
}
if (items.length < 2) return;
// --- Create sidebar in document.body ---
var sidebar = document.createElement("nav");
sidebar.id = "orb-toc-sidebar";
sidebar.setAttribute("aria-label", "Table of Contents");
var head = document.createElement("div");
head.className = "orb-toc-head";
head.textContent =
"\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435";
sidebar.appendChild(head);
var ul = document.createElement("ul");
for (var j = 0; j < items.length; j++) {
var item = items[j];
var li = document.createElement("li");
li.className = "orb-toc-" + item.tag;
li.setAttribute("data-target", item.id);
var a = document.createElement("a");
a.href = "#" + item.id;
a.textContent = item.text;
(function (targetId) {
a.addEventListener("click", function (e) {
var target = document.getElementById(targetId);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
history.replaceState(null, "", "#" + targetId);
}
});
})(item.id);
li.appendChild(a);
ul.appendChild(li);
}
sidebar.appendChild(ul);
// Append to body - outside any Citizen container
document.body.appendChild(sidebar);
// --- Scroll spy ---
var allLi = ul.querySelectorAll("li");
var headingEls = [];
for (var k = 0; k < items.length; k++) {
headingEls.push(items[k].el);
}
var ticking = false;
function onScroll() {
if (ticking) return;
ticking = true;
requestAnimationFrame(function () {
ticking = false;
var current = -1;
for (var s = 0; s < headingEls.length; s++) {
if (headingEls[s].getBoundingClientRect().top <= 120) {
current = s;
}
}
for (var t = 0; t < allLi.length; t++) {
if (t === current) {
allLi[t].classList.add("orb-toc-active");
} else {
allLi[t].classList.remove("orb-toc-active");
}
}
});
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
}
// Run on multiple hooks for skin compat
if (typeof mw !== "undefined" && mw.hook) {
mw.hook("wikipage.content").add(initOrbToc);
}
document.addEventListener("DOMContentLoaded", initOrbToc);
window.addEventListener("load", function () {
setTimeout(initOrbToc, 150);
});
})();