User:Stumblean/common.js: Difference between revisions

Stumblean (talk | contribs)
No edit summary
Tags: Mobile edit Mobile web edit Advanced mobile edit
Stumblean (talk | contribs)
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 with tooltip
   // 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",
   });
   });
  massEditBtn.onclick = () => {
    alert("Mass edit clicked — functionality coming soon!");
  };
  content.appendChild(massEditBtn);


   // Open Explanation button (existing)
   // 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";
  };


   menu.appendChild(content);
   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;


  document.body.appendChild(menu);
    await doMassEdit(pages, text1, text2, editType, summary, minor);
  };


   // Dragging logic
   // Drag logic
   let isDragging = false,
   let isDragging = false,
     offsetX = 0,
     offsetX = 0,
Line 121: Line 331:
   }
   }


  // Mouse events
   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);


  // Touch events
   header.addEventListener("touchstart", function (e) {
   header.addEventListener("touchstart", function (e) {
     const touch = e.touches[0];
     const touch = e.touches[0];