User:SuperGrey/gadgets/Reaction/main.js
外观
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google Chrome、Firefox、Microsoft Edge及Safari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
// Main page: [[User:SuperGrey/gadgets/Reaction]]
// <nowiki>
(() => {
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/state.ts
var State = class {
constructor() {
/**
* 使用者名稱,從MediaWiki配置中獲取。
* @type {string}
* @constant
*/
__publicField(this, "userName", mw.config.get("wgUserName"));
/**
* 頁面名稱,從MediaWiki配置中獲取。
* @type {string}
* @constant
*/
__publicField(this, "pageName", mw.config.get("wgPageName"));
// MediaWiki API 實例
__publicField(this, "_api", null);
// 簡繁轉換
__publicField(this, "convByVar", function(langDict) {
if (langDict && langDict.hant) {
return langDict.hant;
}
return "繁簡轉換未初始化,且 langDict 無效!";
});
}
getApi() {
if (!this._api) {
this._api = new mw.Api({ "User-Agent": "Reaction/1.0.0" });
}
return this._api;
}
initHanAssist() {
return mw.loader.using("ext.gadget.HanAssist").then((require2) => {
const { convByVar } = require2("ext.gadget.HanAssist");
if (typeof convByVar === "function") {
this.convByVar = convByVar;
}
});
}
};
var state = new State();
var state_default = state;
// src/utils.ts
var chineseUtcRegex = `\\d{4}年\\d{1,2}月\\d{1,2}日 \\([日一二三四五六]\\) \\d{1,2}:\\d{2} \\(UTC\\)`;
function escapeRegex(string) {
return mw.util.escapeRegExp(string);
}
function atChineseUtcRegex() {
return "(?:|[於于]" + chineseUtcRegex + ")";
}
function userNameAtChineseUtcRegex() {
return escapeRegex(state_default.userName) + atChineseUtcRegex();
}
function getCurrentChineseUtc() {
const date = /* @__PURE__ */ new Date();
return dateToChineseUtc(date);
}
function parseUtc14(utc14) {
const year = Number(utc14.slice(0, 4));
const month = Number(utc14.slice(4, 6)) - 1;
const day = Number(utc14.slice(6, 8));
const hour = Number(utc14.slice(8, 10));
const minute = Number(utc14.slice(10, 12));
const second = Number(utc14.slice(12, 14));
return new Date(Date.UTC(year, month, day, hour, minute, second));
}
function utc14ToChineseUtc(utc14) {
const date = parseUtc14(utc14);
return dateToChineseUtc(date);
}
function dateToChineseUtc(date) {
return date.getUTCFullYear() + "年" + (date.getUTCMonth() + 1) + "月" + date.getUTCDate() + "日 (" + [
"日",
"一",
"二",
"三",
"四",
"五",
"六"
][date.getUTCDay()] + ") " + date.getUTCHours().toString().padStart(2, "0") + ":" + date.getUTCMinutes().toString().padStart(2, "0") + " (UTC)";
}
function parseTimestamp(timestamp) {
let utcTimestamp = timestamp.querySelector(".localcomments");
if (utcTimestamp) {
return utcTimestamp.getAttribute("title");
} else {
let href = timestamp.getAttribute("href");
let ts_s = href.split("#")[1] || "";
if (ts_s.startsWith("c-")) {
let ts = (ts_s.match(/-(\d{14})/) || [])[1];
if (ts) {
return utc14ToChineseUtc(ts);
}
ts = (ts_s.match(/-(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000Z)/) || [])[1];
if (ts) {
let date = new Date(ts);
return dateToChineseUtc(date);
}
}
console.error("[Reaction] Unable to parse timestamp in: " + href);
return null;
}
}
// src/api.ts
async function retrieveFullText() {
let response = await state_default.getApi().get({
action: "query",
titles: state_default.pageName,
prop: "revisions",
rvslots: "*",
rvprop: "content",
indexpageids: 1
});
let fulltext = response.query.pages[response.query.pageids[0]].revisions[0].slots.main["*"];
return fulltext + "\n";
}
async function saveFullText(fulltext, summary) {
try {
await state_default.getApi().postWithToken("edit", {
action: "edit",
title: state_default.pageName,
text: fulltext,
summary: summary + " ([[User:SuperGrey/gadgets/Reaction|Reaction]])"
});
mw.notify(state_default.convByVar({ hant: "[Reaction] 儲存成功!", hans: "[Reaction] 保存成功!" }), {
title: "成功",
type: "success"
});
return true;
} catch (e) {
console.error(e);
mw.notify(state_default.convByVar({
hant: "[Reaction] 失敗!無法儲存頁面。",
hans: "[Reaction] 失败!无法保存页面。"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
return false;
}
}
async function modifyPage(mod) {
let fulltext;
try {
fulltext = await retrieveFullText();
} catch (e) {
console.error(e);
mw.notify(state_default.convByVar({
hant: "[Reaction] 失敗!無法獲取頁面內容。",
hans: "[Reaction] 失败!无法获取页面内容。"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
return false;
}
let newFulltext;
let summary = "";
try {
let timestampRegex = new RegExp(`${escapeRegex(mod.timestamp)}`, "g");
let timestampMatch = fulltext.match(timestampRegex);
if (!timestampMatch || timestampMatch.length === 0) {
console.log("[Reaction] Unable to find timestamp " + mod.timestamp + " in: " + fulltext);
throw new Error("[Reaction] " + state_default.convByVar({
hant: "原文中找不到時間戳:",
hans: "原文中找不到时间戳:"
}) + mod.timestamp);
}
if (timestampMatch.length > 1) {
console.log("[Reaction] More than one timestamp found: " + timestampMatch);
throw new Error("[Reaction] " + state_default.convByVar({
hant: "原文中找到多個相同的時間戳,小工具無法處理:",
hans: "原文中找到多个相同的时间戳,小工具无法处理:"
}) + mod.timestamp + state_default.convByVar({
hant: "。請手動編輯。",
hans: "。请手动编辑。"
}));
}
let pos = fulltext.search(timestampRegex);
console.log("[Reaction] Found timestamp " + mod.timestamp + " at position " + pos);
if (mod.remove) {
let regex = new RegExp(` *\\{\\{ *[Rr]eact(?:ion|) *\\| *${escapeRegex(mod.remove)} *\\| *${userNameAtChineseUtcRegex()} *}}`, "g");
let lineEnd = fulltext.indexOf("\n", pos);
let timestamp2LineEnd = fulltext.slice(pos, lineEnd);
let newTimestamp2LineEnd = timestamp2LineEnd.replace(regex, "");
newFulltext = fulltext.slice(0, pos) + newTimestamp2LineEnd + fulltext.slice(lineEnd);
summary = "− " + mod.remove;
} else if (mod.downvote) {
let regex = new RegExp(`\\{\\{ *[Rr]eact(?:ion|) *\\| *${escapeRegex(mod.downvote)} *(|\\|[^}]*?)\\| *${userNameAtChineseUtcRegex()} *(|\\|[^}]*?)}}`, "g");
let lineEnd = fulltext.indexOf("\n", pos);
let timestamp2LineEnd = fulltext.slice(pos, lineEnd);
let newTimestamp2LineEnd = timestamp2LineEnd.replace(regex, `{{Reaction|${mod.downvote}$1$2}}`);
newFulltext = fulltext.slice(0, pos) + newTimestamp2LineEnd + fulltext.slice(lineEnd);
summary = "− " + mod.downvote;
} else if (mod.upvote) {
let regex = new RegExp(`\\{\\{ *[Rr]eact(?:ion|) *\\| *${escapeRegex(mod.upvote)}([^}]*?)}}`, "g");
let lineEnd = fulltext.indexOf("\n", pos);
let timestamp2LineEnd = fulltext.slice(pos, lineEnd);
let newTimestamp2LineEnd = timestamp2LineEnd.replace(regex, `{{Reaction|${mod.upvote}$1|${state_default.userName}於${getCurrentChineseUtc()}}}`);
newFulltext = fulltext.slice(0, pos) + newTimestamp2LineEnd + fulltext.slice(lineEnd);
summary = "+ " + mod.upvote;
} else if (mod.append) {
let regex = new RegExp(`\\{\\{ *[Rr]eact(?:ion|) *\\| *${escapeRegex(mod.append)}([^}]*?)}}`, "g");
let lineEnd = fulltext.indexOf("\n", pos);
let timestamp2LineEnd = fulltext.slice(pos, lineEnd);
if (regex.test(timestamp2LineEnd)) {
console.log("[Reaction] Reaction of " + mod.append + " already exists in: " + timestamp2LineEnd);
throw new Error("[Reaction] " + state_default.convByVar({
hant: "原文中已經有這個反應!",
hans: "原文中已经有这个反应!"
}));
}
let newText = "{{Reaction|" + mod.append + "|" + state_default.userName + "於" + getCurrentChineseUtc() + "}}";
newFulltext = fulltext.slice(0, lineEnd) + " " + newText + fulltext.slice(lineEnd);
summary = "+ " + mod.append;
}
if (newFulltext === fulltext) {
console.log("[Reaction] Nothing is modified. Could be because using a template inside {{Reaction}}.");
throw new Error("[Reaction] " + state_default.convByVar({
hant: "原文未被修改。可能是因為使用了嵌套模板;請手動編輯。",
hans: "原文未被修改。可能是因为使用了嵌套模板;请手动编辑。"
}));
}
return await saveFullText(newFulltext, summary);
} catch (e) {
console.error(e);
mw.notify(e.message, { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
return false;
}
}
// src/dom.ts
var _handlerRegistry = /* @__PURE__ */ new WeakMap();
var _buttonTimestamps = /* @__PURE__ */ new WeakMap();
var timestamps = null;
var replyButtons = null;
function handleReactionClick(button) {
if (button.classList.contains("reaction-new")) {
addNewReaction(button);
} else {
if (button.getAttribute("data-reaction-icon-invalid")) {
mw.notify(state_default.convByVar({
hant: "[Reaction] 反應圖示無效,小工具無法處理。",
hans: "[Reaction] 反应图示无效,小工具无法处理。"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
console.error("[Reaction] Invalid reaction icon.");
return;
}
if (typeof window.ujsReactionConfirmedRequired !== "undefined" && window.ujsReactionConfirmedRequired) {
let confirmMessage;
if (button.classList.contains("reaction-reacted")) {
confirmMessage = state_default.convByVar({
hant: "[Reaction] 確定要取消這個反應嗎?",
hans: "[Reaction] 确定要取消这个反应吗?"
});
} else {
confirmMessage = state_default.convByVar({
hant: "[Reaction] 確定要追加這個反應嗎?",
hans: "[Reaction] 确定要追加这个反应吗?"
});
}
OO.ui.confirm(confirmMessage, {
title: state_default.convByVar({ hant: "確認", hans: "确认" }),
size: "small"
}).then((confirmed) => {
if (confirmed) {
toggleReaction(button);
}
});
} else {
toggleReaction(button);
}
}
}
function toggleReaction(button) {
if (button.classList.contains("reaction-reacted")) {
if (!button.getAttribute("data-reaction-commentors").includes(state_default.userName)) {
mw.notify(state_default.convByVar({
hant: "[Reaction] 失敗!不能取消並未做出的反應。",
hans: "[Reaction] 失败!不能取消并未做出的反应。"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
console.log("[Reaction] Should not happen! " + state_default.userName + " should be in " + button.getAttribute("data-reaction-commentors"));
return;
}
let buttonIcon = button.querySelector(".reaction-icon");
let buttonCounter = button.querySelector(".reaction-counter");
let count = parseInt(button.getAttribute("data-reaction-count") || buttonCounter.innerText);
let mod;
if (count > 1) {
mod = {
timestamp: parseTimestamp(_buttonTimestamps.get(button)),
downvote: button.getAttribute("data-reaction-icon").trim() || buttonIcon.innerText.trim()
};
} else {
mod = {
timestamp: parseTimestamp(_buttonTimestamps.get(button)),
remove: button.getAttribute("data-reaction-icon").trim() || buttonIcon.innerText.trim()
};
}
modifyPage(mod).then((response) => {
if (response) {
button.classList.remove("reaction-reacted");
if (count > 1) {
buttonCounter.innerText = (count - 1).toString();
let dataCommentors = button.getAttribute("data-reaction-commentors") + "/";
dataCommentors = dataCommentors.replace(new RegExp(userNameAtChineseUtcRegex() + "/", "g"), "");
dataCommentors = dataCommentors.slice(0, -1);
button.setAttribute("data-reaction-commentors", dataCommentors);
let buttonTitle = button.getAttribute("title");
if (buttonTitle) {
buttonTitle = buttonTitle.replace(new RegExp(userNameAtChineseUtcRegex(), "g"), "");
let trailingSemicolonRegex = new RegExp(";" + atChineseUtcRegex() + "回[應应]了[這这][條条]留言$", "g");
buttonTitle = buttonTitle.replace(trailingSemicolonRegex, "");
let trailingCommaRegex = new RegExp("、" + atChineseUtcRegex() + "(|、.+?)(回[應应]了[這这][條条]留言)$", "g");
buttonTitle = buttonTitle.replace(trailingCommaRegex, "$1$2");
buttonTitle = buttonTitle.replace(new RegExp("^" + atChineseUtcRegex() + "、"), "");
button.setAttribute("title", buttonTitle);
}
} else {
button.parentNode.removeChild(button);
}
}
});
} else {
if (button.getAttribute("data-reaction-commentors").includes(state_default.userName)) {
mw.notify(state_default.convByVar({
hant: "[Reaction] 失敗!不能重複做出反應。",
hans: "[Reaction] 失败!不能重复做出反应。"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
console.log("[Reaction] Should not happen! " + state_default.userName + " should not be in " + button.getAttribute("data-reaction-commentors"));
return;
}
let buttonIcon = button.querySelector(".reaction-icon");
let mod = {
timestamp: parseTimestamp(_buttonTimestamps.get(button)),
upvote: button.getAttribute("data-reaction-icon").trim() || buttonIcon.innerText.trim()
};
modifyPage(mod).then((response) => {
if (response) {
button.classList.add("reaction-reacted");
let buttonCounter = button.querySelector(".reaction-counter");
let count = parseInt(buttonCounter.innerText);
buttonCounter.innerText = (count + 1).toString();
let dataCommentors = button.getAttribute("data-reaction-commentors");
if (dataCommentors) {
dataCommentors += "/" + state_default.userName + "於" + getCurrentChineseUtc();
} else {
dataCommentors = state_default.userName + "於" + getCurrentChineseUtc();
}
button.setAttribute("data-reaction-commentors", dataCommentors);
let buttonTitle = button.getAttribute("title");
if (buttonTitle) {
buttonTitle += ";";
} else {
buttonTitle = "";
}
buttonTitle += state_default.userName + state_default.convByVar({
hant: "於",
hans: "于"
}) + getCurrentChineseUtc() + state_default.convByVar({
hant: "回應了這條留言",
hans: "回应了这条留言"
});
button.setAttribute("title", buttonTitle);
}
});
}
}
function cancelNewReaction(button, event) {
if (event) {
event.stopPropagation();
}
let saveButton = button.querySelector(".reaction-save");
const saveButtonClickHandler = _handlerRegistry.get(saveButton);
if (saveButtonClickHandler) {
saveButton.removeEventListener("click", saveButtonClickHandler);
_handlerRegistry.delete(saveButton);
}
let cancelButton = button.querySelector(".reaction-cancel");
const cancelButtonClickHandler = _handlerRegistry.get(cancelButton);
if (cancelButtonClickHandler) {
cancelButton.removeEventListener("click", cancelButtonClickHandler);
_handlerRegistry.delete(cancelButton);
}
let buttonIcon = button.querySelector(".reaction-icon");
buttonIcon.textContent = "+";
let buttonCounter = button.querySelector(".reaction-counter");
buttonCounter.innerText = state_default.convByVar({ hant: "反應", hans: "反应" });
if (_handlerRegistry.has(button)) {
console.error("[Reaction] Not possible! The event handler should not be registered yet.");
return;
}
const buttonClickHandler = handleReactionClick.bind(this, button);
_handlerRegistry.set(button, buttonClickHandler);
button.addEventListener("click", buttonClickHandler);
}
function saveNewReaction(button, event) {
if (event) {
event.stopPropagation();
}
let input = button.querySelector(".reaction-icon input");
if (!input.value.trim()) {
mw.notify(state_default.convByVar({
hant: "[Reaction] 反應內容不能為空!",
hans: "[Reaction] 反应内容不能为空!"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
return;
}
let timestamp = parseTimestamp(_buttonTimestamps.get(button));
if (!timestamp) {
mw.notify(state_default.convByVar({
hant: "[Reaction] 失敗!無法獲取時間戳。",
hans: "[Reaction] 失败!无法获取时间戳。"
}), { title: state_default.convByVar({ hant: "錯誤", hans: "错误" }), type: "error" });
return;
}
let mod = {
timestamp,
append: input.value.trim()
};
modifyPage(mod).then((response) => {
if (response) {
button.classList.remove("reaction-new");
button.classList.add("reaction-reacted");
let buttonIcon = button.querySelector(".reaction-icon");
buttonIcon.textContent = input.value;
let buttonCounter = button.querySelector(".reaction-counter");
buttonCounter.textContent = "1";
button.setAttribute("title", state_default.userName + state_default.convByVar({
hant: "於",
hans: "于"
}) + getCurrentChineseUtc() + state_default.convByVar({
hant: "回應了這條留言",
hans: "回应了这条留言"
}));
button.setAttribute("data-reaction-commentors", state_default.userName);
let saveButton = button.querySelector(".reaction-save");
const saveButtonClickHandler = _handlerRegistry.get(saveButton);
if (saveButtonClickHandler) {
saveButton.removeEventListener("click", saveButtonClickHandler);
_handlerRegistry.delete(saveButton);
}
let cancelButton = button.querySelector(".reaction-cancel");
const cancelButtonClickHandler = _handlerRegistry.get(cancelButton);
if (cancelButtonClickHandler) {
cancelButton.removeEventListener("click", cancelButtonClickHandler);
_handlerRegistry.delete(cancelButton);
}
let newReactionButton = NewReactionButton();
button.parentNode.insertBefore(newReactionButton, button.nextSibling);
_buttonTimestamps.set(newReactionButton, _buttonTimestamps.get(button));
if (_handlerRegistry.has(button)) {
console.error("Not possible! The event handler should not be registered yet.");
return;
}
const buttonClickHandler = handleReactionClick.bind(this, button);
_handlerRegistry.set(button, buttonClickHandler);
button.addEventListener("click", buttonClickHandler);
}
});
}
function ResizableInput(text = "", parent = document.body) {
let input = document.createElement("input");
input.value = text;
input.style.width = "1em";
input.style.background = "transparent";
input.style.border = "0";
input.style.boxSizing = "content-box";
parent.appendChild(input);
let hiddenInput = document.createElement("span");
hiddenInput.style.position = "absolute";
hiddenInput.style.top = "0";
hiddenInput.style.left = "0";
hiddenInput.style.visibility = "hidden";
hiddenInput.style.height = "0";
hiddenInput.style.overflow = "scroll";
hiddenInput.style.whiteSpace = "pre";
parent.appendChild(hiddenInput);
const inputStyles = window.getComputedStyle(input);
[
"fontFamily",
"fontSize",
"fontWeight",
"fontStyle",
"letterSpacing",
"textTransform"
].forEach((prop) => {
hiddenInput.style[prop] = inputStyles[prop];
});
function inputResize() {
hiddenInput.innerText = input.value || input.placeholder || text;
const width = hiddenInput.scrollWidth;
input.style.width = width + 2 + "px";
}
input.addEventListener("input", inputResize);
inputResize();
return input;
}
function addNewReaction(button) {
const buttonClickHandler = _handlerRegistry.get(button);
if (buttonClickHandler) {
button.removeEventListener("click", buttonClickHandler);
_handlerRegistry.delete(button);
}
let buttonIcon = button.querySelector(".reaction-icon");
buttonIcon.textContent = "";
let input = ResizableInput("👍", buttonIcon);
input.focus();
input.select();
input.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
saveNewReaction(button, false);
} else if (event.key === "Escape") {
cancelNewReaction(button, false);
}
});
let buttonCounter = button.querySelector(".reaction-counter");
let saveButton = document.createElement("span");
saveButton.className = "reaction-save";
saveButton.innerText = state_default.convByVar({ hant: "儲存", hans: "保存" });
if (_handlerRegistry.has(saveButton)) {
return;
}
const saveButtonClickHandler = saveNewReaction.bind(this, button);
_handlerRegistry.set(saveButton, saveButtonClickHandler);
saveButton.addEventListener("click", saveButtonClickHandler);
let cancelButton = document.createElement("span");
cancelButton.className = "reaction-cancel";
cancelButton.innerText = state_default.convByVar({ hant: "取消", hans: "取消" });
if (_handlerRegistry.has(cancelButton)) {
return;
}
const cancelButtonClickHandler = cancelNewReaction.bind(this, button);
_handlerRegistry.set(cancelButton, cancelButtonClickHandler);
cancelButton.addEventListener("click", cancelButtonClickHandler);
buttonCounter.innerText = "";
buttonCounter.appendChild(saveButton);
buttonCounter.appendChild(document.createTextNode(" | "));
buttonCounter.appendChild(cancelButton);
}
function NewReactionButton() {
let button = document.createElement("span");
button.className = "reactionable template-reaction reaction-new";
let buttonContent = document.createElement("span");
buttonContent.className = "reaction-content";
let buttonIconContainer = document.createElement("span");
buttonIconContainer.className = "reaction-icon-container";
let buttonIcon = document.createElement("span");
buttonIcon.className = "reaction-icon";
buttonIcon.innerText = "+";
buttonIconContainer.appendChild(buttonIcon);
let buttonCounterContainer = document.createElement("span");
buttonCounterContainer.className = "reaction-counter-container";
let buttonCounter = document.createElement("span");
buttonCounter.className = "reaction-counter";
buttonCounter.innerText = state_default.convByVar({ hant: "反應", hans: "反应" });
buttonCounterContainer.appendChild(buttonCounter);
buttonContent.appendChild(buttonIconContainer);
buttonContent.appendChild(buttonCounterContainer);
button.appendChild(buttonContent);
let buttonClickHandler = handleReactionClick.bind(this, button);
_handlerRegistry.set(button, buttonClickHandler);
button.addEventListener("click", buttonClickHandler);
return button;
}
function bindEvent2ReactionButton(button) {
if (_handlerRegistry.has(button)) {
return;
}
let buttonClickHandler = handleReactionClick.bind(this, button);
_handlerRegistry.set(button, buttonClickHandler);
button.addEventListener("click", buttonClickHandler);
let reacted = false;
for (const commentor of button.getAttribute("data-reaction-commentors").split("/")) {
let regex = new RegExp("^" + userNameAtChineseUtcRegex() + "$");
if (regex.test(commentor)) {
reacted = true;
break;
}
}
if (reacted) {
button.classList.add("reaction-reacted");
}
}
function addReactionButtons() {
if (document.querySelector("#reaction-finished-loading")) {
return;
}
timestamps = document.querySelectorAll("a.ext-discussiontools-init-timestamplink");
replyButtons = document.querySelectorAll("span.ext-discussiontools-init-replylink-buttons");
for (let i = 0; i < timestamps.length; i++) {
let timestamp = timestamps[i];
let replyButton = replyButtons[i];
let button = timestamp.nextElementSibling;
while (button && button !== replyButton) {
if (button.classList.contains("template-reaction") && button.attributes["data-reaction-commentors"]) {
_buttonTimestamps.set(button, timestamp);
bindEvent2ReactionButton(button);
}
button = button.nextElementSibling;
}
}
for (let i = 0; i < replyButtons.length; i++) {
let reactionButton = NewReactionButton();
let timestamp = timestamps[i];
_buttonTimestamps.set(reactionButton, timestamp);
let replyButton = replyButtons[i];
replyButton.parentNode.insertBefore(reactionButton, replyButton);
}
console.log(`[Reaction] Added ${replyButtons.length} new reaction buttons.`);
let finishedLoading = document.createElement("div");
finishedLoading.id = "reaction-finished-loading";
finishedLoading.style.display = "none";
document.querySelector("#mw-content-text .mw-parser-output").appendChild(finishedLoading);
}
// src/main.ts
function init() {
mw.loader.load("/w/index.php?title=Template:Reaction/styles.css&action=raw&ctype=text/css", "text/css");
state_default.initHanAssist().then(() => {
mw.hook("wikipage.content").add(function() {
setTimeout(() => addReactionButtons(), 200);
});
});
}
init();
})();
// </nowiki>