MediaWiki:Common.js: Difference between revisions
Appearance
No edit summary |
No edit summary Tag: Reverted |
||
| Line 109: | Line 109: | ||
/* Jakes Recent Changes Portlet */ | /* Jakes Recent Changes Portlet */ | ||
(function () { | (function () { | ||
/* ---------------- Recent changes ---------------- */ | |||
var jakeRcPortletId = "p-jake-recentchanges"; | |||
var jakeRcListId = "jake-rc-list"; | |||
var jakeRcIntervalId = null; | |||
var jakeRcObserver = null; | |||
function jakeTimeAgo(date) { | function jakeTimeAgo(date) { | ||
var s = Math.floor((Date.now() - date.getTime()) / 1000); | var s = Math.floor((Date.now() - date.getTime()) / 1000); | ||
| Line 120: | Line 127: | ||
} | } | ||
function | function jakeGetMenu() { | ||
return ( | |||
document.getElementById("vector-main-menu") || | document.getElementById("vector-main-menu") || | ||
document.getElementById("mw-panel"); | document.querySelector("#mw-panel #vector-main-menu") || | ||
document.getElementById("mw-panel") || | |||
null | |||
); | |||
} | |||
function jakeBuildPortlet() { | |||
var menu = jakeGetMenu(); | |||
if (!menu) return null; | if (!menu) return null; | ||
if (document.getElementById(" | |||
var existingList = document.getElementById(jakeRcListId); | |||
if (existingList) return existingList; | |||
var existingPortlet = document.getElementById(jakeRcPortletId); | |||
if (existingPortlet) { | |||
var list = existingPortlet.querySelector("#" + jakeRcListId); | |||
return list || null; | |||
} | |||
var portlet = document.createElement("div"); | var portlet = document.createElement("div"); | ||
portlet.id = | portlet.id = jakeRcPortletId; | ||
portlet.className = "vector-menu mw-portlet"; | portlet.className = "vector-menu mw-portlet"; | ||
| Line 141: | Line 162: | ||
var ul = document.createElement("ul"); | var ul = document.createElement("ul"); | ||
ul.className = "vector-menu-content-list"; | ul.className = "vector-menu-content-list"; | ||
ul.id = | ul.id = jakeRcListId; | ||
var moreWrap = document.createElement("div"); | var moreWrap = document.createElement("div"); | ||
| Line 149: | Line 170: | ||
moreLink.href = mw.util.getUrl("Special:RecentChanges"); | moreLink.href = mw.util.getUrl("Special:RecentChanges"); | ||
moreLink.textContent = "Show more…"; | moreLink.textContent = "Show more…"; | ||
moreWrap.appendChild(moreLink); | moreWrap.appendChild(moreLink); | ||
content.appendChild(ul); | content.appendChild(ul); | ||
content.appendChild(moreWrap); | content.appendChild(moreWrap); | ||
| Line 157: | Line 178: | ||
portlet.appendChild(content); | portlet.appendChild(content); | ||
var | var after = | ||
menu.querySelector("#p-Community") || | |||
menu.querySelector("#p-navigation") || | |||
null; | |||
if (after && after.parentNode) after.parentNode.insertBefore(portlet, after.nextSibling); | |||
else menu.appendChild(portlet); | |||
return ul; | return ul; | ||
} | } | ||
function | function jakeRenderRecentChanges(items) { | ||
var ul = document.getElementById(jakeRcListId) || jakeBuildPortlet(); | |||
if (!ul) return; | if (!ul) return; | ||
ul.textContent = ""; | |||
if (!items || !items.length) { | |||
var li0 = document.createElement("li"); | |||
li0.className = "mw-list-item jake-rc-item"; | |||
var a0 = document.createElement("a"); | |||
a0.href = mw.util.getUrl("Special:RecentChanges"); | |||
a0.textContent = "View recent changes"; | |||
li0.appendChild(a0); | |||
ul.appendChild(li0); | |||
return; | |||
} | |||
for (var i = 0; i < items.length; i++) { | |||
var rc = items[i]; | |||
var li = document.createElement("li"); | |||
li.className = "mw-list-item jake-rc-item"; | |||
var a = document.createElement("a"); | |||
a.href = mw.util.getUrl(rc.title); | |||
a.textContent = rc.title; | |||
var meta = document.createElement("div"); | |||
meta.className = "jake-rc-meta"; | |||
meta.textContent = jakeTimeAgo(new Date(rc.timestamp)) + " · " + (rc.user || ""); | |||
li.appendChild(a); | |||
li.appendChild(meta); | |||
ul.appendChild(li); | |||
} | |||
} | } | ||
function | function jakeFetchRecentChanges() { | ||
var ul = document.getElementById(jakeRcListId) || jakeBuildPortlet(); | |||
if (!ul) return; | |||
var | var api = new mw.Api(); | ||
api.get({ | |||
action: "query", | |||
list: "recentchanges", | |||
} | rcnamespace: "0|4", | ||
rclimit: 5, | |||
rctype: "edit|new", | |||
rcprop: "title|timestamp|user", | |||
rcshow: "!bot", | |||
formatversion: 2 | |||
}).then(function (data) { | |||
var items = | |||
data && data.query && data.query.recentchanges | |||
? data.query.recentchanges | |||
: []; | |||
jakeRenderRecentChanges(items); | |||
}).catch(function () { | |||
jakeRenderRecentChanges([]); | |||
}); | |||
} | } | ||
function | function jakeWatchRecentChangesPortlet() { | ||
if (jakeRcObserver) return; | |||
jakeRcObserver = new MutationObserver(function () { | |||
var menu = | var menu = jakeGetMenu(); | ||
if (!menu) return; | |||
if ( | if (!document.getElementById(jakeRcPortletId)) { | ||
jakeBuildPortlet(); | |||
jakeFetchRecentChanges(); | |||
} | } | ||
}); | |||
jakeRcObserver.observe(document.body, { childList: true, subtree: true }); | |||
} | } | ||
function jakeInitRecentChanges() { | |||
jakeBuildPortlet(); | |||
jakeFetchRecentChanges(); | |||
if (jakeRcIntervalId) clearInterval(jakeRcIntervalId); | |||
jakeRcIntervalId = setInterval(jakeFetchRecentChanges, 60000); | |||
jakeWatchRecentChangesPortlet(); | |||
} | |||
/* */ | /* */ | ||
(function () { | (function () { | ||
Revision as of 00:22, 28 January 2026
/* Any JavaScript here will be loaded for all users on every page load. */
// stolen from Lajos Mészáros on this post https://stackoverflow.com/questions/13358292/capture-tap-event-with-pure-javascript
const onClickOrTap = (element, handler) => {
let touchMoveHappened = false;
element.addEventListener('touchstart', () => {
// on mobile this is the 1st event that happens
touchMoveHappened = false;
});
element.addEventListener('touchmove', () => {
// on mobile this might get triggered in which case the
// click or tap will get cancelled
// we'll keep a track of it
touchMoveHappened = true;
});
element.addEventListener('touchend', (e) => {
// happens after touchstart, but before click
// if touch happened then we'll exit
if (touchMoveHappened) {
return;
}
// calling preventDefault() will make sure the
// subsequent click will not get triggered
e.preventDefault();
// at this point we are ready to call our original handler
handler(e);
});
element.addEventListener('click', (e) => {
// this will only get triggered on desktop
// because we call preventDefault for the "touchend" event
handler(e);
});
}
/* Create Page */
const createPageTargetIdDataSetString = "[data-create-page-target-id]";
if (document.querySelector(createPageTargetIdDataSetString)) {
/**
* @type {HTMLElement[]}
*/
let itemsCreateInputPageGrid = [];
let itemsCreatePageGrid = [];
const createPageTargetId = "createPageTargetId";
const createPageInputHiddenClass = "hidden";
const createPageInputActiveClass = "pageInputActive";
/**
* @param {HTMLElement} element
*/
function getGridTarget(element) {
const targetId = element.dataset[createPageTargetId];
return document.getElementById(targetId);
}
/**
* @this {HTMLDivElement}
*/
function toggleGridTarget(element, targetElement) {
for (let index = 0; index < itemsCreateInputPageGrid.length; index += 1) {
const target = itemsCreateInputPageGrid[index];
target.classList.add(createPageInputHiddenClass);
if (target === targetElement) {
target.classList.remove(createPageInputHiddenClass);
}
const clickedElement = itemsCreatePageGrid[index];
clickedElement.classList.remove(createPageInputActiveClass);
if (clickedElement === element) {
clickedElement.classList.add(createPageInputActiveClass);
}
}
}
/**
* @param {HTMLElement} element
* @param {HTMLElement} targetElement
*/
function addToggleGridTargetEvent(element, targetElement) {
itemsCreatePageGrid.push(element);
itemsCreateInputPageGrid.push(targetElement);
onClickOrTap(element, (e) => {
toggleGridTarget(element, targetElement)
});
//element.addEventListener("click", () =>
// toggleGridTarget(element, targetElement)
//);
}
const createPageTargetElements = document.querySelectorAll(
createPageTargetIdDataSetString
);
for (let index = 0; index < createPageTargetElements.length; index += 1) {
const element = createPageTargetElements[index];
const targetElement = getGridTarget(element);
if (targetElement) {
addToggleGridTargetEvent(element, targetElement);
}
}
}
/* Jakes Recent Changes Portlet */
(function () {
/* ---------------- Recent changes ---------------- */
var jakeRcPortletId = "p-jake-recentchanges";
var jakeRcListId = "jake-rc-list";
var jakeRcIntervalId = null;
var jakeRcObserver = null;
function jakeTimeAgo(date) {
var s = Math.floor((Date.now() - date.getTime()) / 1000);
if (s < 60) return s + "s ago";
var m = Math.floor(s / 60);
if (m < 60) return m + "m ago";
var h = Math.floor(m / 60);
if (h < 24) return h + "h ago";
var d = Math.floor(h / 24);
return d + "d ago";
}
function jakeGetMenu() {
return (
document.getElementById("vector-main-menu") ||
document.querySelector("#mw-panel #vector-main-menu") ||
document.getElementById("mw-panel") ||
null
);
}
function jakeBuildPortlet() {
var menu = jakeGetMenu();
if (!menu) return null;
var existingList = document.getElementById(jakeRcListId);
if (existingList) return existingList;
var existingPortlet = document.getElementById(jakeRcPortletId);
if (existingPortlet) {
var list = existingPortlet.querySelector("#" + jakeRcListId);
return list || null;
}
var portlet = document.createElement("div");
portlet.id = jakeRcPortletId;
portlet.className = "vector-menu mw-portlet";
var heading = document.createElement("div");
heading.className = "vector-menu-heading";
heading.textContent = "Recent changes";
var content = document.createElement("div");
content.className = "vector-menu-content";
var ul = document.createElement("ul");
ul.className = "vector-menu-content-list";
ul.id = jakeRcListId;
var moreWrap = document.createElement("div");
moreWrap.className = "jake-rc-more";
var moreLink = document.createElement("a");
moreLink.href = mw.util.getUrl("Special:RecentChanges");
moreLink.textContent = "Show more…";
moreWrap.appendChild(moreLink);
content.appendChild(ul);
content.appendChild(moreWrap);
portlet.appendChild(heading);
portlet.appendChild(content);
var after =
menu.querySelector("#p-Community") ||
menu.querySelector("#p-navigation") ||
null;
if (after && after.parentNode) after.parentNode.insertBefore(portlet, after.nextSibling);
else menu.appendChild(portlet);
return ul;
}
function jakeRenderRecentChanges(items) {
var ul = document.getElementById(jakeRcListId) || jakeBuildPortlet();
if (!ul) return;
ul.textContent = "";
if (!items || !items.length) {
var li0 = document.createElement("li");
li0.className = "mw-list-item jake-rc-item";
var a0 = document.createElement("a");
a0.href = mw.util.getUrl("Special:RecentChanges");
a0.textContent = "View recent changes";
li0.appendChild(a0);
ul.appendChild(li0);
return;
}
for (var i = 0; i < items.length; i++) {
var rc = items[i];
var li = document.createElement("li");
li.className = "mw-list-item jake-rc-item";
var a = document.createElement("a");
a.href = mw.util.getUrl(rc.title);
a.textContent = rc.title;
var meta = document.createElement("div");
meta.className = "jake-rc-meta";
meta.textContent = jakeTimeAgo(new Date(rc.timestamp)) + " · " + (rc.user || "");
li.appendChild(a);
li.appendChild(meta);
ul.appendChild(li);
}
}
function jakeFetchRecentChanges() {
var ul = document.getElementById(jakeRcListId) || jakeBuildPortlet();
if (!ul) return;
var api = new mw.Api();
api.get({
action: "query",
list: "recentchanges",
rcnamespace: "0|4",
rclimit: 5,
rctype: "edit|new",
rcprop: "title|timestamp|user",
rcshow: "!bot",
formatversion: 2
}).then(function (data) {
var items =
data && data.query && data.query.recentchanges
? data.query.recentchanges
: [];
jakeRenderRecentChanges(items);
}).catch(function () {
jakeRenderRecentChanges([]);
});
}
function jakeWatchRecentChangesPortlet() {
if (jakeRcObserver) return;
jakeRcObserver = new MutationObserver(function () {
var menu = jakeGetMenu();
if (!menu) return;
if (!document.getElementById(jakeRcPortletId)) {
jakeBuildPortlet();
jakeFetchRecentChanges();
}
});
jakeRcObserver.observe(document.body, { childList: true, subtree: true });
}
function jakeInitRecentChanges() {
jakeBuildPortlet();
jakeFetchRecentChanges();
if (jakeRcIntervalId) clearInterval(jakeRcIntervalId);
jakeRcIntervalId = setInterval(jakeFetchRecentChanges, 60000);
jakeWatchRecentChangesPortlet();
}
/* */
(function () {
function jakeAddFeedbackToNamespaces() {
var list = document.querySelector("#p-associated-pages .vector-menu-content-list");
if (!list) return;
var existing = document.getElementById("jake-feedback-btn");
if (existing && !existing.closest("#p-associated-pages")) {
var oldLi = existing.closest("li");
if (oldLi) oldLi.remove();
else existing.remove();
}
if (document.getElementById("ca-jake-feedback")) return;
var li = document.createElement("li");
li.id = "ca-jake-feedback";
li.className = "vector-tab-noicon mw-list-item";
var a = document.createElement("a");
a.id = "jake-feedback-btn";
a.className = "jake-feedback-btn";
a.href = mw.util.getUrl("Consumer_Rights_Wiki:Feedback", {
from: mw.config.get("wgPageName")
});
a.title = "Give feedback";
var span = document.createElement("span");
span.textContent = "Give feedback";
a.appendChild(span);
li.appendChild(a);
list.appendChild(li);
}
function jakeInit() {
if (!window.mw || !mw.util || !mw.config) return;
jakeAddFeedbackToNamespaces();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", jakeInit);
} else {
jakeInit();
}
})();