Tags: Mobile edit Mobile web edit Advanced mobile edit |
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 21: |
Line 20: |
| }); | | }); |
|
| |
|
| // Header
| |
| const header = document.createElement("div"); | | const header = document.createElement("div"); |
| Object.assign(header.style, { | | Object.assign(header.style, { |
Line 32: |
Line 30: |
| }); | | }); |
|
| |
|
| // Left icon
| |
| const leftIcon = document.createElement("img"); | | const leftIcon = document.createElement("img"); |
| leftIcon.src = iconURL; | | leftIcon.src = iconURL; |
Line 40: |
Line 37: |
| leftIcon.style.borderRadius = "3px"; | | leftIcon.style.borderRadius = "3px"; |
|
| |
|
| // Title text
| |
| const title = document.createElement("div"); | | const title = document.createElement("div"); |
| title.textContent = "Mahito Menu"; | | title.textContent = "Mahito Menu"; |
| title.style.fontWeight = "bold"; | | title.style.fontWeight = "bold"; |
|
| |
|
| // Right icon
| |
| const rightIcon = document.createElement("img"); | | const rightIcon = document.createElement("img"); |
| rightIcon.src = iconURL; | | rightIcon.src = iconURL; |
Line 56: |
Line 51: |
| header.appendChild(title); | | header.appendChild(title); |
| header.appendChild(rightIcon); | | header.appendChild(rightIcon); |
|
| |
| menu.appendChild(header); | | menu.appendChild(header); |
|
| |
|
| // Content area
| |
| const content = document.createElement("div"); | | const content = document.createElement("div"); |
|
| |
|
| // Mass edit button
| |
| const massEditBtn = document.createElement("button"); | | const massEditBtn = document.createElement("button"); |
| massEditBtn.textContent = "Mass edit"; | | massEditBtn.textContent = "Mass edit"; |
Line 76: |
Line 68: |
| textAlign: "left", | | textAlign: "left", |
| }); | | }); |
| | massEditBtn.onclick = () => { |
| | alert("Mass edit clicked (not implemented)"); |
| | }; |
|
| |
|
| // Open Explanation button
| |
| const openBtn = document.createElement("button"); | | const openBtn = document.createElement("button"); |
| openBtn.textContent = "Open Explanation"; | | openBtn.textContent = "Open Explanation"; |
Line 89: |
Line 83: |
| textAlign: "left", | | textAlign: "left", |
| }); | | }); |
| openBtn.onclick = () => console.log("Open Explanation clicked"); | | openBtn.onclick = () => { |
| | console.log("Open Explanation clicked"); |
| | }; |
|
| |
|
| content.appendChild(massEditBtn); | | content.appendChild(massEditBtn); |
| content.appendChild(openBtn); | | content.appendChild(openBtn); |
| menu.appendChild(content); | | menu.appendChild(content); |
| | |
| document.body.appendChild(menu); | | 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(new RegExp("[.*+?|(){}\\\\\", "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 | | // Drag logic |