User:What7what8/darkmodefixtool.js
外观
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google Chrome、Firefox、Microsoft Edge及Safari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
//WIP
//TODO: Fix bug (link in temp dont work cuz replaceBetween)
//Fixed: Fix big bug (unable to use when page has gallery element in wikitext, Example:元素周期表, 等离子体)
//TODO: Fix big bug (make buggy change at some page, Example: 氧)
$(document).ready(function() {
$.getJSON(mw.config.get("wgServer") + mw.config.get("wgScriptPath") + "/api.php?action=query&prop=revisions&format=json&titles=" + mw.config.get("wgPageName") + "&rvlimit=1&rvslots=*&rvprop=content|ids|timestamp&meta=tokens&type=csrf", function(data) {
let token = data.query.tokens.csrftoken
var js2 = data.query.pages[mw.config.get("wgArticleId")].revisions[0];
let baserevid = js2.revid
let basetimestamp = js2.timestamp
const wikitext = js2.slots.main['*'];
/*if (/<gallery.*?>[\s\S]*?<\/gallery>/gim.test(wikitext)){
mw.notify("this page is too powerful for this small tool")
return;
}*/
// REGEX
var imgLink = /(?:File:|Image:)([\s\S]+?(?:\.png|\.jpg|\.svg|\.jpeg|\.gif|\.tiff|\.webp|\.xcf|\.pdf))/gim;
var imgTemp = /([\s\S]+?(\.png|\.jpg|\.svg|\.jpeg|\.gif|\.tiff|\.webp|\.xcf|\.pdf))/gim;
var linkAndTemp = /\[\[[\s\S]+?\]\]|{{[\s\S]+?}}/gim;
function waitForElm(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
function isRangeInRange(innerStart, innerEnd, outerStart, outerEnd) {
return innerStart >= outerStart && innerEnd <= outerEnd;
}
function replaceBetween(input, replacement) {
replacement = replacement.filter(function(currentValue, index, array) {
let [text, start, end] = currentValue;
if (input.substring(start, end) === text) {
return false
}
for (let i = 0; i < array.length; i++) {
let [text1, start1, end1] = array[i];
if (i !== index && isRangeInRange(start, end, start1, end1)) {
return false
}
}
return true
})
let res = [];
let i = 0;
for (let k = 0; k < replacement.length; k++) {
let [text, start, end] = replacement[k];
res.push(input.slice(i, start) + text);
i = end;
}
res.push(input.slice(i));
return res.join("")
}
function extractNestedStrings(inputStr, pattern = "[]", include = false) {
//extract template or link from wikitext, example: 'abc[[a[[b]][[[aaa]]][abc]cde[[f[[g]]]]]]abc' -> ['b', '[aaa', 'g', 'f[[g]]', 'a[[b]][[[aaa]]][abc]cde[[f[[g]]]]']
let skipIndex = []
let skipPatt = /(?:<\s*?nowiki[\s\S]*?>[\s\S]*?<\s*?\/\s*?nowiki\s*?>)|(?:<\s*?math[\s\S]*?>[\s\S]*?<\s*?\/\s*?math\s*?>)|<!--[\s\S]*?-->/igm;
while (mathEle = skipPatt.exec(inputStr)) {
skipIndex.push([mathEle.index,skipPatt.lastIndex])
}
var openIndices = [];
var openBracketCount = 0;
var closeBracketCount = 0;
var result = [];
main: for (var i = 0; i < inputStr.length; i++) {
for (var j = 0; j < skipIndex.length; j++) {
if (i >= skipIndex[j][0] && i < skipIndex[j][1]) {
continue main;
}
}
var char = inputStr.charAt(i);
if (char === pattern.charAt(0)) {
openBracketCount++;
if (openBracketCount === 2) {
openBracketCount = 0;
openIndices.push(include ? i - 1 : i + 1);
}
} else {
openBracketCount = 0;
}
if (char === pattern.charAt(1)) {
closeBracketCount++;
if (closeBracketCount === 2) {
closeBracketCount = 0;
result.push(
[inputStr.substring(openIndices[openIndices.length - 1], include ? i + 1 : i - 1),
openIndices[openIndices.length - 1],
include ? i + 1 : i - 1
]);
openIndices.pop();
}
} else {
closeBracketCount = 0;
}
}
return result;
}
function filterImage(mode) {
var result = [];
var pattern = (mode === 'temp') ? '{}' : '[]';
const elist = extractNestedStrings(wikitext , pattern);
for (var i = 0; i < elist.length; i++) {
const [ele, start, end] = elist[i];
const e = ele.replaceAll("{{skin-invert}}", "|class=skin-invert").replaceAll(linkAndTemp, "")
console.log((ele.includes('gallery')) ? ele : '')
//need rewrite this
var args = e.split("|");
for (var j = 0; j < args.length; j++) {
var arg = args[j];
var r = [...arg.split("=").at(-1).matchAll(((mode === 'temp') ? imgTemp : imgLink))]
if (r.length !== 0 && !r[0][1].includes("://")) {
let inverted = false
if (mode === 'temp') {
if (arg.includes("skin-invert") || (j + 1 !== args.length && /class\s*=.*(skin-invert|skin-invert-image)/.test(args[j + 1]))) {
inverted = true
}
} else {
if (e.includes('skin-invert')) {
inverted = true
}
}
var secondInvertWarn = 0;
if (e.includes('skin-invert') && mode === 'temp') {
secondInvertWarn = 1;
if (!inverted) {
secondInvertWarn = 2;
}
}
let code = ele.replaceAll("\n", "</nowiki><br /><nowiki>").replaceAll(r[0][1], '</nowiki><span style="color: orange">' + r[0][1] + '</span><nowiki>').replaceAll("skin-invert-image", '</nowiki><span style="color: cyan">skin-invert-image</span><nowiki>').replaceAll(/skin-invert(?!-image)/g, '</nowiki><span style="color: cyan">skin-invert</span><nowiki>')
result.push({
'target': r[0][1].replace("File:", "").replace("Image:", ""),
'inverted': inverted,
'warn': [mode === 'temp' ? e.includes("class") : false, secondInvertWarn],
'code': (mode === 'temp') ? '{{' + code + '}}' : '[[' + code + ']]'
});
}
}
}
return result;
}
function genOutput(mode) {
let gen = ""
let list = filterImage(mode)
for (var i = 0; i < list.length; i++) {
var e = list[i];
var warn = '';
if (e.warn[0] === true) {
warn += '{{Ombox|type = notice|text = 留意反色有否干擾原來的class}}';
}
if (e.warn[1] === 1) {
warn += '{{Ombox|type = notice|text = 留意有否二次反色}}';
} else if (e.warn[1] === 2) {
warn += '{{Ombox|type = content|text = 注意可能二次反色}}';
}
gen += '{{hr|5}}' + warn + '{{HideH|代碼:}}<nowiki>' + e.code + '</nowiki>{{HideF}} <div style="background-color: ' + (mode === 'temp' ? '#202122' : "#101418") + ';"><div class="toolimg' + (e.inverted ? ' skin-invert' : '') + '" data-target="' + e.target + '" data-before-change-invert=' + e.inverted + ' data-mode=' + mode + '> [[File:' + e.target + '|250x250px]]</div></div>';
}
return gen
}
var output = genOutput("temp") + genOutput("link");
var url = mw.config.get("wgServer") + mw.config.get("wgScriptPath") + '/api.php';
var myobj = {
"action": "parse",
"format": "json",
"formatversion": 2,
"text": output,
"pst": "true",
"disableeditsection": "true",
"disabletoc": "true",
"uselang": mw.config.get("wgPageViewLanguage"),
"useskin": "vector-2022",
"contentmodel": "wikitext",
"prop": "text"
};
$.ajax({
url: url,
type: 'POST',
data: myobj,
success: function(data) {
$('body').append('<div id="toolimg-panel">' + data.parse.text + '</div>')
$(".toolimg").each(function(index) {
$(this).after('<input type="checkbox" id="toolimg-checkbox-' + index + '"/><label style="color: white;" for="toolimg-checkbox-' + index + '">反色</label><button type="button" id="toolimg-eyedropper-' + index + '">測試背景顏色</button>')
if ($(this).hasClass("skin-invert")) {
$('#toolimg-checkbox-' + index)[0].checked = true;
} else $('#toolimg-checkbox-' + index)[0].checked = false;
let toolimg = $(this)
$('#toolimg-checkbox-' + index).on('change', function() {
if (this.checked) {
if (!toolimg.hasClass("skin-invert")) toolimg.addClass("skin-invert")
} else {
if (toolimg.hasClass("skin-invert")) toolimg.removeClass("skin-invert")
}
});
const eyeDropper = new EyeDropper()
$('#toolimg-eyedropper-' + index).on('click', async function() {
try {
const selectedColor = await eyeDropper.open();
toolimg.parent().attr("style", "background-color: " + selectedColor.sRGBHex + ";")
} catch (err) {
console.log('eye dropper cancelled')
}
})
})
try{
$("#toolimg-panel").dialog({
autoOpen: false,
height: 1080 * 0.5,
buttons: {
"提交": function() {
let changelist = []
$(".toolimg").each(function(index) {
changelist.push([$(this).attr('data-target'), $(this).attr("data-mode"), $(this).hasClass("skin-invert"), $(this).attr('data-before-change-invert') === 'true' ? true : false]);
})
let newwikitext = wikitext
let linklist = extractNestedStrings(wikitext, "[]", true)
let templist = extractNestedStrings(wikitext, "{}", true)
let lastReach = 0
let replacementList = []
for (let s = 0; s < templist.length; s++) {
let [temp, start, end] = templist[s]
let templistArg = temp.split("|")
for (let i = 0; i < templistArg.length; i++) {
for (let j = lastReach; j < changelist.length; j++) {
let [target, mode, afterChangeInvert, beforeChangeInvert] = changelist[j]
if (templistArg[i].includes(target) && mode === 'temp') {
//change inverted to not invert
if (afterChangeInvert === false && beforeChangeInvert === true) {
if (templistArg[i].includes("{{skin-invert}}")) {
templistArg[i] = templistArg[i].replace("{{skin-invert}}", "")
} else if (i + 1 !== templistArg.length && /class\s*=.*skin-invert/.test(templistArg[i + 1])) {
templistArg[i] = templistArg[i].replace("skin-invert-image", "").replace("skin-invert", "")
}
//change not inverted to invert
} else if (afterChangeInvert === true && beforeChangeInvert === false) {
if (i + 1 !== templistArg.length && /class\s*=.*/.test(templistArg[i + 1]) && !(/class\s*=.*skin-invert/.test(templistArg[i + 1]))) {
templistArg[i + 1] += " skin-invert"
} else if (!templistArg[i].includes("{{skin-invert}}")) {
templistArg[i] = templistArg[i].replace(target, target + "{{skin-invert}}")
}
}
lastReach++
continue
}
}
}
replacementList.push([templistArg.join('|'), start, end])
}
for (let i = 0; i < linklist.length; i++) {
let [link, start, end] = linklist[i]
for (let j = lastReach; j < changelist.length; j++) {
let [target, mode, afterChangeInvert, beforeChangeInvert] = changelist[j]
if (link.includes(target) && mode === 'link') {
//change inverted to not invert
if (afterChangeInvert === false && beforeChangeInvert === true) {
if (link.includes("{{skin-invert}}")) {
link = link.replace("{{skin-invert}}", "")
} else if (/class\s*=.*skin-invert/.test(link)) {
link = link.replace("skin-invert-image", "").replace("skin-invert", "")
}
//change not inverted to invert
} else if (afterChangeInvert === true && beforeChangeInvert === false) {
if (/class\s*=.*/.test(link) && !(/class\s*=.*skin-invert/.test(link))) {
link = link.replace(/class\s*=(.*?)\|/, "class=$1 skin-invert-image|")
} else {
link = link.replace(target, target + "|class=skin-invert-image")
}
}
lastReach++
continue
}
}
replacementList.push([link, start, end])
}
$(this).dialog("close");
var editurl = mw.config.get("wgServer") + mw.config.get("wgScriptPath") + '/api.php';
var editobj = {
"action": "edit",
"title": mw.config.get("wgPageName"),
"format": "json",
"formatversion": 2,
"variant": "zh",
"text": replaceBetween(newwikitext, replacementList),
"summary": "dark mode fix (skin-invert)",
"token": token,
"basetimestamp": basetimestamp,
"baserevid": baserevid,
"nocreate": true,
"minor": true,
"uselang": "zh",
"useskin": "vector-2022"
};
$.ajax({
url: editurl,
type: 'POST',
data: editobj,
success: function(data) {
location.reload();
mw.notify("修改成功了!!!!!")
}
});
}
}
})
} catch (err) {
$("#toolimg-panel").css('display','none');
console.error(err)
}
},
error: function(error) {
console.error(error);
}
});
});
$(document).ajaxStop(function() {
if (!$("#darkmodefix").length && $('#toolimg-panel').length) {
mw.util.addPortlet('darkmodefix', 'Dark Mode Fix Tool', '#skin-client-prefs-skin-theme');
const p = mw.util.addPortletLink('darkmodefix', "javascript:;", 'Skin-invert Image Tool')
let open = false
$(p).on("click", function() {
if ($('#toolimg-panel').length) {
if (!open) {
$("#toolimg-panel").dialog("open");
open = true
} else {
$("#toolimg-panel").dialog("close");
open = false
}
}
});
} else {
$("#toolimg-panel").css('display','none');
}
});
})