User:Stumblean/common.js: Difference between revisions
No edit summary Tags: Mobile edit Mobile web edit Advanced mobile edit |
No edit summary Tags: Mobile edit Mobile web edit Advanced mobile edit |
||
Line 2: | Line 2: | ||
const iconURL = "https://files.catbox.moe/wk78nl.jpg"; | const iconURL = "https://files.catbox.moe/wk78nl.jpg"; | ||
// Create menu | |||
const menu = document.createElement("div"); | const menu = document.createElement("div"); | ||
menu.id = "mahitoMenu"; | menu.id = "mahitoMenu"; | ||
Line 20: | Line 21: | ||
}); | }); | ||
// Header | |||
const header = document.createElement("div"); | const header = document.createElement("div"); | ||
Object.assign(header.style, { | Object.assign(header.style, { | ||
Line 57: | Line 59: | ||
menu.appendChild(header); | menu.appendChild(header); | ||
// Content area | |||
const content = document.createElement("div"); | const content = document.createElement("div"); | ||
// Mass edit button | // Mass edit button | ||
const massEditBtn = document.createElement("button"); | const massEditBtn = document.createElement("button"); | ||
massEditBtn.textContent = "Mass edit"; | massEditBtn.textContent = "Mass edit"; | ||
Line 73: | Line 76: | ||
textAlign: "left", | textAlign: "left", | ||
}); | }); | ||
// Open Explanation button | // Open Explanation button | ||
const openBtn = document.createElement("button"); | const openBtn = document.createElement("button"); | ||
openBtn.textContent = "Open Explanation"; | openBtn.textContent = "Open Explanation"; | ||
Line 91: | Line 90: | ||
}); | }); | ||
openBtn.onclick = () => console.log("Open Explanation clicked"); | openBtn.onclick = () => console.log("Open Explanation clicked"); | ||
content.appendChild(massEditBtn); | |||
content.appendChild(openBtn); | content.appendChild(openBtn); | ||
menu.appendChild(content); | |||
document.body.appendChild(menu); | |||
// Modal overlay & form for mass edit | |||
const modal = document.createElement("div"); | |||
Object.assign(modal.style, { | |||
display: "none", | |||
position: "fixed", | |||
top: 0, | |||
left: 0, | |||
width: "100vw", | |||
height: "100vh", | |||
backgroundColor: "rgba(0,0,0,0.8)", | |||
zIndex: "10000", | |||
justifyContent: "center", | |||
alignItems: "center", | |||
}); | |||
const form = document.createElement("form"); | |||
Object.assign(form.style, { | |||
backgroundColor: "#222", | |||
padding: "20px", | |||
borderRadius: "8px", | |||
color: "#fff", | |||
maxWidth: "400px", | |||
width: "90%", | |||
display: "flex", | |||
flexDirection: "column", | |||
gap: "10px", | |||
}); | |||
form.innerHTML = ` | |||
<label>Pages to edit (one per line):</label> | |||
<textarea id="massEditPages" rows="5" style="width:100%;"></textarea> | |||
<label>Text 1:</label> | |||
<textarea id="massEditText1" rows="3" style="width:100%;"></textarea> | |||
<label>Text 2:</label> | |||
<textarea id="massEditText2" rows="3" style="width:100%;"></textarea> | |||
<label>Edit type:</label> | |||
<select id="massEditType" style="width:100%;"> | |||
<option value="prepend">Prepend text 1</option> | |||
<option value="append">Append text 1</option> | |||
<option value="bothpend">Prepend text 1 and append text 2</option> | |||
<option value="replacetext">Replace first instance of text 1 with text 2</option> | |||
<option value="replacetextg">Replace all instances of text 1 with text 2</option> | |||
<option value="replacepage">Replace page with text 1</option> | |||
</select> | |||
<label>Edit summary:</label> | |||
<input id="massEditSummary" type="text" style="width:100%;" /> | |||
<label><input id="massEditMinor" type="checkbox" /> Mark edit as minor</label> | |||
<button type="submit" style="padding:8px; background:#555; border:none; color:#fff; cursor:pointer;"> | |||
Submit Mass Edit | |||
</button> | |||
<button type="button" id="massEditCancel" style="padding:8px; background:#333; border:none; color:#fff; cursor:pointer;"> | |||
Cancel | |||
</button> | |||
<div id="massEditResult" style="margin-top:10px;"></div> | |||
`; | |||
modal.appendChild(form); | |||
document.body.appendChild(modal); | |||
massEditBtn.onclick = () => { | |||
modal.style.display = "flex"; | |||
}; | |||
document.getElementById("massEditCancel").onclick = (e) => { | |||
e.preventDefault(); | |||
modal.style.display = "none"; | |||
document.getElementById("massEditResult").textContent = ""; | |||
}; | |||
// Mass Edit function (your code slightly adapted for async) | |||
async function doMassEdit(pages, text1, text2, editType, summary, minor) { | |||
let edited = 0; | |||
const failed = []; | |||
const errors = []; | |||
function getPageText(title) { | |||
return fetch( | |||
mw.config.get("wgScriptPath") + | |||
"/api.php?action=query&prop=revisions&rvprop=content&format=json&indexpageids=1&titles=" + | |||
encodeURIComponent(title) | |||
) | |||
.then((res) => res.json()) | |||
.then((response) => { | |||
const pageid = response.query.pageids[0]; | |||
if (pageid === "-1") return ""; | |||
return response.query.pages[pageid].revisions[0]["*"]; | |||
}); | |||
} | |||
for (let article of pages) { | |||
if (!article.trim()) continue; | |||
try { | |||
// Get token | |||
const tokenResp = await fetch( | |||
mw.config.get("wgScriptPath") + | |||
"/api.php?format=json&action=query&prop=info&meta=tokens&type=csrf&titles=" + | |||
encodeURIComponent(article) | |||
); | |||
const tokenJson = await tokenResp.json(); | |||
const edittoken = tokenJson.query.tokens.csrftoken; | |||
// Get original page text if needed | |||
let newPageText = ""; | |||
if ( | |||
editType === "replacetext" || | |||
editType === "replacetextg" || | |||
editType === "bothpend" | |||
) { | |||
let pagetext = await getPageText(article); | |||
if (editType === "replacetextg") { | |||
const escapedText1 = text1.replace(/[.*+?|(){}\/g, "\\$&"); | |||
const reg = new RegExp(escapedText1, "g"); | |||
pagetext = pagetext.replace(reg, text2); | |||
} else if (editType === "replacetext") { | |||
pagetext = pagetext.replace(text1, text2); | |||
} else { | |||
pagetext = text1 + pagetext + text2; | |||
} | |||
newPageText = pagetext; | |||
} | |||
let postData = new URLSearchParams(); | |||
postData.append("format", "json"); | |||
postData.append("action", "edit"); | |||
postData.append("watchlist", "nochange"); | |||
postData.append("title", article); | |||
postData.append("summary", summary); | |||
postData.append("token", edittoken); | |||
if (minor) postData.append("minor", "1"); | |||
else postData.append("notminor", "1"); | |||
if (editType === "prepend") { | |||
postData.append("prependtext", text1 + "\n"); | |||
} else if (editType === "append") { | |||
postData.append("appendtext", "\n" + text1); | |||
} else if (editType === "replacepage") { | |||
postData.append("text", text1); | |||
} else if ( | |||
editType === "replacetext" || | |||
editType === "replacetextg" || | |||
editType === "bothpend" | |||
) { | |||
postData.append("text", newPageText); | |||
} | |||
const editResp = await fetch(mw.config.get("wgScriptPath") + "/api.php", { | |||
method: "POST", | |||
headers: { | |||
"Content-Type": "application/x-www-form-urlencoded", | |||
}, | |||
body: postData.toString(), | |||
}); | |||
const editJson = await editResp.json(); | |||
if (editJson.edit) { | |||
edited++; | |||
document.getElementById("massEditResult").textContent = | |||
`Edited ${edited} pages...`; | |||
} else { | |||
failed.push(article); | |||
errors.push(editJson.error?.info || "Unknown error"); | |||
} | |||
} catch (err) { | |||
failed.push(article); | |||
errors.push(err.message); | |||
} | |||
} | |||
// Show summary | |||
if (failed.length > 0) { | |||
document.getElementById("massEditResult").innerHTML = | |||
`<b>Done with errors.</b><br>Edited: ${edited}<br>Failed:` + | |||
failed | |||
.map( | |||
(page, i) => | |||
`<div>${page}: ${errors[i]}</div>` | |||
) | |||
.join(""); | |||
} else { | |||
document.getElementById("massEditResult").innerHTML = | |||
`<b>All done!</b><br>Edited: ${edited}`; | |||
} | |||
} | |||
// Handle form submit | |||
form.onsubmit = async (e) => { | |||
e.preventDefault(); | |||
document.getElementById("massEditResult").textContent = "Working..."; | |||
const pages = document | |||
.getElementById("massEditPages") | |||
.value.split("\n") | |||
.map((x) => x.trim()) | |||
.filter((x) => x); | |||
const text1 = document.getElementById("massEditText1").value; | |||
const text2 = document.getElementById("massEditText2").value; | |||
const editType = document.getElementById("massEditType").value; | |||
const summary = document.getElementById("massEditSummary").value; | |||
const minor = document.getElementById("massEditMinor").checked; | |||
await doMassEdit(pages, text1, text2, editType, summary, minor); | |||
}; | |||
// | // Drag logic | ||
let isDragging = false, | let isDragging = false, | ||
offsetX = 0, | offsetX = 0, | ||
Line 121: | Line 331: | ||
} | } | ||
header.addEventListener("mousedown", function (e) { | header.addEventListener("mousedown", function (e) { | ||
startDrag(e.clientX, e.clientY); | startDrag(e.clientX, e.clientY); | ||
Line 133: | Line 342: | ||
document.addEventListener("mouseup", stopDrag); | document.addEventListener("mouseup", stopDrag); | ||
header.addEventListener("touchstart", function (e) { | header.addEventListener("touchstart", function (e) { | ||
const touch = e.touches[0]; | const touch = e.touches[0]; |