Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
(function ($, mw) {
  "use strict";

  mw.loader.using(["mediawiki.util", "mediawiki.api"]).then(function () {
    const api = new mw.Api();

    console.log("GlaMainBot gallery.js loaded");

    const nameToFileName = (name) => {
      name = name.replace(/ /g, "_");
      name = name
        .split(".")
        .map((x, i, arr) => (i === arr.length - 1 ? x.toLowerCase() : x))
        .join(".");
      return name;
    };

    const fileNameToName = (name, names) => {
      return names.find((x) => nameToFileName(x) === name);
    };

    const glamaingallery = document.querySelectorAll(".glamaingallery");

    const bulkUpload = (links) => {
      if (document.getElementById("bulkUploadContainer")) {
        return;
      }

      const names = links.map((x) => {
        const url = new URL(x.href);
        return url.searchParams.get("wpDestFile");
      });

      const fileList = (files) => {
        return files
          .map(([name, color]) => {
            color = color ? color : "black";
            return `<a href="/wiki/File:${fileNameToName(
              name,
              names
            )}" target="_blank" style="color: ${color}">${name}</a>`;
          })
          .join("<br>");
      };

      const fileNames = names.map(nameToFileName);

      const container = document.createElement("div");

      container.id = "bulkUploadContainer";
      container.style.position = "fixed";
      container.style.top = "50%";
      container.style.left = "50%";
      container.style.transform = "translate(-50%, -50%)";
      container.style.zIndex = "1000";
      container.style.backgroundColor = "white";
      container.style.padding = "20px";
      container.style.border = "1px solid black";
      container.style.width = "100%";
      container.style.maxWidth = "400px";

      container.innerHTML = `
		  <form method="post">
			  <h2>Bulk upload</h2>
			  <input type="file" name="file" multiple style="display: block; margin-bottom: 10px">
			  <small style="display: block; margin-bottom: 10px; font-size: 10px;">Only files with the following names are allowed: <br><span></span></small>
			  <input type="text" name="comment" placeholder="Removed watermark" style="display: block; margin-bottom: 10px">
			  <input type="submit" value="Upload" disabled style="display: block; margin-bottom: 10px">
			  <pre style="font-size: 10px; display: block; white-space: pre-wrap; margin-bottom: 10px"></pre>
			  <button type="button" onclick="this.closest('div').remove()" style="display: block; margin-bottom: 10px">Close</button>
		  </form>
		`;

      document.body.appendChild(container);

      const form = container.querySelector("form");
      const input = form.querySelector("input[type=file]");
      const commentInput = form.querySelector("input[name=comment]");
      const small = form.querySelector("small");
      const span = small.querySelector("span");
      const submit = form.querySelector("input[type=submit]");
      const pre = form.querySelector("pre");
      pre.textContent =
        "Note: If the file is not in the list, it will be ignored\n";

      span.innerHTML = fileList(fileNames.map((x) => [x, ""]));

      let files = [];

      input.addEventListener("change", (e) => {
        const allFiles = Array.from(e.target.files);
        files = allFiles.filter((x) => fileNames.includes(x.name));
        const notAllowedFiles = allFiles.filter(
          (x) => !fileNames.includes(x.name)
        );
        span.innerHTML = fileList([
          ...fileNames.map((x) => [
            x,
            files.map((x) => x.name).includes(x) ? "green" : "",
          ]),
          ...notAllowedFiles.map((x) => [x.name, "red"]),
        ]);
        submit.value = `Upload (${files.length}/${allFiles.length})`;
        submit.disabled = files.length === 0;

        pre.textContent += `Selected ${files.length} files\n`;
      });

      form.addEventListener("submit", (e) => {
        e.preventDefault();
        if (submit.disabled || files.length === 0) {
          return;
        }

        input.disabled = true;
        submit.disabled = true;

        const comment = commentInput.value
          ? commentInput.value
          : commentInput.placeholder;

        pre.textContent += `Uploading ${files.length} files...\n`;

        const fileColors = fileNames.map((x) => [x, ""]);
        const setFileColor = (name, color) => {
          fileColors[fileColors.findIndex((x) => x[0] === name)][1] = color;
          span.innerHTML = fileList(fileColors);
        };

        span.innerHTML = fileList(fileColors);

        const successes = [];

        const upload = (i) => {
          if (i >= files.length) {
            pre.textContent += `Uploaded ${successes.length}/${files.length} files\n`;
            for (const success of successes) {
              window.open(success, "_blank");
            }
            return;
          }

          const file = files[i];

          pre.textContent += `Uploading ${file.name} (${i + 1}/${files.length})...\n`;

          const formData = new FormData();
          formData.append("token", mw.user.tokens.get("csrfToken"));
          formData.append("action", "upload");
          formData.append("format", "json");
          formData.append("file", file);
          formData.append("filename", fileNameToName(file.name, names));
          formData.append("comment", comment);
          formData.append("ignorewarnings", 1);

          fetch("/w/api.php", {
              method: "POST",
              body: formData,
            })
              .then((x) => x.json())
              .then((response) => {
                console.debug(`Upload response for ${file.name}`, response);

                if (
                  response &&
                  response.upload &&
                  response.upload.result === "Success"
                ) {
                  pre.textContent += `Uploaded ${file.name} (${i + 1}/${files.length})\n`;
                  setFileColor(file.name, "green");
                  successes.push(response.upload.imageinfo.descriptionurl);
                } else {
                  pre.textContent += `Failed to upload ${file.name} (${i + 1}/${files.length})\n`;
                  if (
                    response &&
                    response.upload &&
                    response.upload.warnings
                  ) {
                    pre.textContent += `${file.name} warnings: ${result.upload.warnings.join(", ")}\n`;
                  }
                  setFileColor(file.name, "red");
                }
                upload(i + 1);
              })
              .catch((e) => {
                console.error(e);
                pre.textContent += `${file.name} error: ${e}\n`;
                setFileColor(file.name, "red");
                upload(i + 1);
              });
        };
        
        upload(0);
      });

      input.click();
    };

    glamaingallery.forEach((glamaingallery) => {
      const items = glamaingallery.querySelectorAll(
        "li.gallerybox div.gallerytext"
      );
      items.forEach((item) => {
        const links = item.querySelectorAll("a");
        links.forEach((link) => {
          const url = link.getAttribute("href");
          if (url.includes("croptool") || url.includes("Special:Upload")) {
            link.setAttribute("target", "_blank");
          }
        });
      });
      const bulkLinks = Array.from(
        glamaingallery.querySelectorAll(".gallerytext .plainlinks a")
      );
      for (const link of bulkLinks) {
        const bulkLink = link.cloneNode(true);
        bulkLink.textContent = "+6";
        link.after(bulkLink);
        link.after(document.createTextNode(" "));

        bulkLink.addEventListener("click", (e) => {
          e.preventDefault();
          const sameLinks = bulkLinks.filter(
            (x) => x.textContent === link.textContent
          );
          const links = sameLinks.slice(
            sameLinks.indexOf(link),
            sameLinks.indexOf(link) + 7
          );

          if (link.href.includes("Special:Upload")) {
            bulkUpload(links);
            return;
          }

          links.forEach((el) => {
            window.open(el.getAttribute("href") || "", "_blank");
          });
        });
      }
    });
  });
})(jQuery, mw);