User:BlankEclair/SpecialUploadPrefillFromComic.js

From TestWiki
Revision as of 09:34, 14 June 2024 by BlankEclair (talk | contribs) (Bring inline with wiki version)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
"use strict";

/* MISCELLANEOUS FUNCTIONS */
//const SUPC_MESSAGES = require("./SpecialUploadPrefillFromComic-i18n.json");
const SUPC_MESSAGES = {
	"en": {
		"close-button": "Close",
		"continue-button": "Continue",
		"prefill-description-from-comic-page-button": "Prefill description from comic page",
		"prefill-dialog-title": "Prefill description",
		"required-fields-unfilled": "Please fill in all required fields",
		"cannot-find-link": "Failed to find direct link to the comic's source",
		"cannot-find-synopsis": "Failed to automatically locate comic page synopsis",
		"unexpected-error": "An unexpected error occured",

		"overrides-description-warning": "Warning: Going through this prefill process will erase all text currently in the description field",
		"page-input-header": "What comic page is this image from? (required)",
		"description-select-header": "Image description:",
		"description-auto": "Obtain from comic page automatically",
		"description-manual": "Enter manually",
		"description-input-placeholder": "e.g. Rain from Rain reading Rain",
		"source-select-header": "Where was this image taken from? (required)",
		"deviantart": "Deviantart",
		"smackjeeves": "SmackJeeves",
		"comicfury": "Comicfury",
		"tapas": "Tapas",
		"image-type-header": "What type of image is this? (sets license)",
		"image-unspecified": "Unspecified (does not set license)",
		"image-comic": "A comic page",
		"image-crop": "Part of a comic page"
	}
};
function getMessage(key) {
    var languageCode = document.documentElement.lang;
    if (languageCode === "qqx") {
        return "(" + key + ")";
    }

    var messages = SUPC_MESSAGES[languageCode] || SUPC_MESSAGES.en;
    return messages[key] || SUPC_MESSAGES.en[key] || "(" + key + ")";
}

function getDefaultDescription() {
    return document.querySelector("#wpUploadDescription").textContent;
}
function getCurrentDescription() {
    return document.querySelector("#wpUploadDescription").value;
}
function setCurrentDescription(text) {
    document.querySelector("#wpUploadDescription").value = text;
}

function setLicense(key) {
    document.querySelector("#wpLicense").value = key;
}

/* PREFILL DIALOG */
function PrefillDialog(config) {
    PrefillDialog.super.call(this, config);
}
OO.inheritClass(PrefillDialog, OO.ui.ProcessDialog);

PrefillDialog.static.name = "prefillDialog";
PrefillDialog.static.title = getMessage("prefill-dialog-title");
PrefillDialog.static.actions = [
    {
        label: getMessage("close-button"),
        flags: ["safe", "close"],
    },
    {
        action: "continue",
        label: getMessage("continue-button"),
        flags: ["primary", "progressive"],
    },
];

PrefillDialog.prototype.initialize = function() {
    PrefillDialog.super.prototype.initialize.apply(this, arguments);

    this.content = new OO.ui.PanelLayout({
        padded: true,
        expanded: false,
    });
    if (getCurrentDescription() !== getDefaultDescription()) {
        var p = document.createElement("p");
        p.innerText = getMessage("overrides-description-warning");
        this.content.$element.append(p, document.createElement("hr"));
    }

    this.pageInput = new mw.widgets.TitleInputWidget({
        required: true,
        showMissing: false,
    });
    var pageField = new OO.ui.FieldLayout(this.pageInput, {
        label: getMessage("page-input-header"),
        align: "top",
    });
    this.content.$element.append(pageField.$element);

    this.descriptionSourceSelect = new OO.ui.RadioSelectWidget({
        items: [
            new OO.ui.RadioOptionWidget({
                data: "auto",
                label: getMessage("description-auto"),
            }),
            new OO.ui.RadioOptionWidget({
                data: "manual",
                selected: true,
                label: getMessage("description-manual"),
            }),
        ],
    });
    var descriptionSourceField = new OO.ui.FieldLayout(this.descriptionSourceSelect, {
        label: getMessage("description-select-header"),
        align: "top",
    });
    descriptionSourceField.$element.addClass("description-source-field-layout");
    this.content.$element.append(descriptionSourceField.$element);

    this.manualDescriptionInput = new OO.ui.MultilineTextInputWidget({
        placeholder: getMessage("description-input-placeholder"),
    });
    this.manualDescriptionInput.$element.addClass("manual-description-input");
    this.content.$element.append(this.manualDescriptionInput.$element);

    this.sourceSelect = new OO.ui.RadioSelectWidget({
        items: [
            new OO.ui.RadioOptionWidget({
                data: "deviantart",
                label: getMessage("deviantart"),
            }),
            new OO.ui.RadioOptionWidget({
                data: "smackjeeves",
                label: getMessage("smackjeeves"),
            }),
            new OO.ui.RadioOptionWidget({
                data: "comic-fury",
                label: getMessage("comicfury"),
            }),
            new OO.ui.RadioOptionWidget({
                data: "tapas",
                label: getMessage("tapas"),
            }),
        ],
    });
    var sourceField = new OO.ui.FieldLayout(this.sourceSelect, {
        label: getMessage("source-select-header"),
        align: "top",
    });
    this.content.$element.append(sourceField.$element);

    this.typeSelect = new OO.ui.RadioSelectWidget({
        items: [
            new OO.ui.RadioOptionWidget({
                selected: true,
                label: getMessage("image-unspecified"),
            }),
            new OO.ui.RadioOptionWidget({
                data: "Comic",
                label: getMessage("image-comic"),
            }),
            new OO.ui.RadioOptionWidget({
                data: "Copyright FairUse",
                label: getMessage("image-crop"),
            }),
        ],
    });
    var typeField = new OO.ui.FieldLayout(this.typeSelect, {
        label: getMessage("image-type-header"),
        align: "top",
    });
    this.content.$element.append(typeField.$element);

    this.$body.append(this.content.$element);
};

PrefillDialog.prototype.requiredFieldsFilled = function() {
    return this.pageInput.getValue() && this.sourceSelect.findSelectedItem();
};

PrefillDialog.prototype.getActionProcess = function(action) {
    var dialog = this;
    if (action === "continue") {
        if (!this.requiredFieldsFilled()) {
            mw.notify(getMessage("required-fields-unfilled"), {type: "error"});
            return new OO.ui.Process(function() {});
        }

        var pageTitle = this.pageInput.getValue();
        var automaticImageDescription = this.descriptionSourceSelect.findSelectedItem().getData() === "auto";
        var imageDescription = this.manualDescriptionInput.getValue();
        var sourceClass = this.sourceSelect.findSelectedItem().getData();
        var licenseToSet = this.typeSelect.findSelectedItem();
        licenseToSet = licenseToSet ? licenseToSet.getData() : null;

        // Why isn't this using mw.Api? Because I keep breaking abstractions (seriously, is there no way I could get an AJAX response from JQuery without it parsing and potentially throwing an error?)
        var search = new URLSearchParams({
            action: "parse",
            page: pageTitle,
            prop: automaticImageDescription ? "text|wikitext" : "text",
            format: "json",
            formatversion: 2,
        });
        var url = mw.config.get("wgScriptPath") + "/api.php?" + search.toString();
        return new OO.ui.Process(fetch(url).then(function(resp) {
            return resp.json();
        }).then(function(data) {
            if (data.error) {
                return mw.notify(data.error.info, {type: "error"});
            }

            var template = document.createElement("template");
            template.innerHTML = data.parse.text;
            var a = template.content.querySelector("a:has(." + sourceClass + ")");
            if (!a) {
                return mw.notify(getMessage("cannot-find-link"), {type: "error"});
            }
            var sourceLink = a.href;
            var date = template.content.querySelector(".pi-item[data-source=published] .pi-data-value");
            date = date && date.innerText !== "Unknown" ? date.innerText : "";

            var spacedTitle = data.parse.title.replace(":", ": ");
            if (automaticImageDescription) {
                var match = /^\s*==\s*Synopsis\s*==\s*\n([\s\S]+?)\s+\n\s*==/m.exec(data.parse.wikitext);
                if (!match) {
                    return mw.notify(getMessage("cannot-find-synopsis"), {type: "error"});
                }
                imageDescription = match[1];
            }

            var newDescription = getDefaultDescription();
            newDescription = newDescription.replace(/(\|\s*description\s*=\s*)(?=\n)/, function(match) {
                return match + imageDescription;
            });
            newDescription = newDescription.replace(/(\|\s*source\s*=\s*)(?=\n)/, function(match) {
                return match + "[[" + spacedTitle + "]] ([" + sourceLink + " link])";
            });
            newDescription = newDescription.replace(/(\|\s*date\s*=\s*)(?=\n)/, function(match) {
                return match + date;
            });
            setCurrentDescription(newDescription);
            if (licenseToSet) {
                setLicense(licenseToSet);
            }
            dialog.close();
        }).catch(function(error) {
            console.log(error);
            mw.notify(error.message, {type: "error", title: getMessage("unexpected-error")});
        }));
    }

    return PrefillDialog.super.prototype.getActionProcess.call(this, action);
};

PrefillDialog.prototype.getBodyHeight = function() {
    return this.content.$element.outerHeight(true);
};

/* INIT CODE */
function prefillButtonActivated(windowManager) {
    var prefillDialog = new PrefillDialog({
        size: "medium",
    });
    windowManager.addWindows([prefillDialog]);
    windowManager.openWindow(prefillDialog);
}

function injectPrefillButton(windowManager) {
    var textarea = document.querySelector("#wpUploadDescription");

    var button = document.createElement("button");
    button.type = "button"; // Explicitly set the button's type as a button, otherwise it will inherit its submit status from the form it's injected into
    button.innerText = getMessage("prefill-description-from-comic-page-button");
    button.addEventListener("click", function(event) {
        event.preventDefault();
        prefillButtonActivated(windowManager);
    });
    textarea.parentElement.append(button);
}

if (document.body.classList.contains("mw-special-Upload") && !document.querySelector("#wpForReUpload")) {
    var windowManager = new OO.ui.WindowManager();
    $(document.body).append(windowManager.$element);

    injectPrefillButton(windowManager);
}