User:Terasail/Edit Request Tool.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
//<nowiki>
/*jshint esversion: 6*/
var editRequestBoxes = document.getElementsByClassName("plainlinks tmbox tmbox-notice editrequest");
var pageWatchers = "There are currently ";
var watchStatus;
var dataERT;
var api = new mw.Api();
var pageNameERT = mw.config.get('wgPageName');
var encodePageName = encodeURIComponent(pageNameERT);
var emptyTalkParams = null;
pageNameERT = pageNameERT.replace(/_/g, " ");

function postEdit(wikitext, editSummary, secIndx, changeWatch, watchlist) {
    let apiParams = {
        action: 'edit',
        title: pageNameERT,
        text: wikitext,
        section: secIndx,
        summary: editSummary,
        watchlist: changeWatch
    };
    let reloadURL = "/w/index.php?title=" + encodePageName + "&type=revision&diff=cur&oldid=prev";
    if (changeWatch == "watch") {
        apiParams.watchlistexpiry = watchlist;
    }
    api.postWithEditToken(apiParams).done(function () {
        if (emptyTalkParams != null) {
            api.postWithEditToken(emptyTalkParams).done(function (result) {
                window.location = reloadURL;
            });
        } else {
            window.location = reloadURL;
        }
    });
}

function execute(responseTable, replyOption, inputText, answered, boxType, targets, changeWatch, watchlist) {
    OO.ui.confirm("Confirm in order to reply to this edit request.").done(function (confirmed) {
        if (confirmed) {
            //Create label box & remove action buttons
            let firstRow = responseTable.children[1].children[0];
            firstRow.innerHTML = "";
            responseTable.children[5].children[0].remove();
            let infoBox = new OO.ui.MessageWidget({
                icon: 'pageSettings',
                type: 'notice',
                label: 'Processing request — Edit request starting, getting section data to edit.'
            });
            infoBox.$element[0].style = "margin:5px 0; max-width:50em";
            $(firstRow).append(infoBox.$element);
            //Create loading bar
            let progressBar = new OO.ui.ProgressBarWidget({
                progress: false
            });
            $(firstRow).append(progressBar.$element);
            //Set preview for output
            if (replyOption[0] != "") {//Don't preview a non-response
                showOutput(inputText, replyOption, responseTable.children[4].children[0], boxType);
            }
            //Find header
            let header = "";
            let curElement = responseTable.parentNode;
            do {
                curElement = curElement.previousElementSibling;
                if (curElement.getElementsByClassName("mw-headline").length == 1) {
                    header = curElement.getElementsByClassName("mw-headline")[0].id;
                }
            }
            while (header == "");
            api.get({
                action: "parse",
                page: pageNameERT,
                prop: "sections"
            }).done(function (data) {
                infoBox.setLabel("Processing request — Making changes to the edit request");
                let editTemplate = "{{Edit " + boxType + "-protected";
                if (answered) {
                    answered = "yes";
                } else {
                    answered = "no";
                }
                for (let c3 = 0; c3 < targets.length; c3++) {
                    editTemplate += "|" + targets[c3];
                }
                editTemplate += "|answered=" + answered;
                let sections = data.parse.sections;
                let secIndx = sections[0];
                for (let j = 0; j < sections.length; j++) {
                    if (sections[j].anchor == header) {
                        secIndx = parseInt(sections[j].index);
                    }
                }
                api.get({
                    action: "parse",
                    page: pageNameERT,
                    section: secIndx,
                    prop: "wikitext|revid"
                }).done(function (data) {
                    let wikitext = data.parse.wikitext["*"];
                    let newRev = data.parse.revid;
                    wikitext = wikitext.replace(/{{ *([SETFI]PER|Edit([ -]?[A-Z]+[ -]?|[- ])Protected)\s*[^}}]*/i, editTemplate);
                    let editSummary = "/* " + header.replaceAll("_", " ") + " */ " + replyOption[2] + " ([[User:Terasail/Edit_Request_Tool|Edit Request Tool]])";
                    if (replyOption[1] != "Close") {
                        wikitext = wikitext.trim() + "\n:";
                        if (replyOption[0] != "") {
                            wikitext += "{{subst:" + dataERT.protections[boxType][1] + replyOption[0] + "}} ";
                        }
                        if (inputText != "") {
                            wikitext += inputText.replaceAll(/\s*~~~~\s*/g, "") + " ";
                        }
                        wikitext += "~~~~";
                        if (replyOption[1] == "Remove") {
                            wikitext = "";
                            /*if (inputText == "EmptyNotice") {
                                let commentERT = document.getElementById(header).dataset.mwComment;
                                if (typeof (commentERT) != 'undefined' && commentERT.search(/-(?=[0-9]{14}","type)/) != -1) {
                                    let commenter = commentERT.slice(commentERT.indexOf('"name"') + 10, commentERT.search(/-(?=[0-9]{14}","type)/));
                                    emptyTalkParams = dataERT.emptyNotice;
                                    emptyTalkParams.title = "User talk:" + commenter;
                                    emptyTalkParams.sectiontitle += pageNameERT;
                                    emptyTalkParams.text = talkParams.text.replace("```", pageNameERT);
                                    emptyTalkParams.summary = talkParams.summary.replace("```", pageNameERT);
                                    console.log(emptyTalkParams);
                                }
                            }*/
                            editSummary = editSummary.replace(/[^]+\*\/ /, "");
                        }
                    }
                    infoBox.setType("success");
                    infoBox.setLabel("Processing request — Saving changes to the talk page.");
                    if (newRev == mw.config.values.wgRevisionId) {
                        postEdit(wikitext, editSummary, secIndx, changeWatch, watchlist);
                    } else {
                        OO.ui.confirm("There has been a new revision to the page, do you wish to continue?").done(function (revCon) {
                            if (revCon) {
                                postEdit(wikitext, editSummary, secIndx, changeWatch, watchlist);
                            }
                        });
                    }
                });
            });
        }
    });
}

function addButtons(currentBox, replyButton) {
    let boxType = currentBox.parentElement.dataset.origlevel;
    boxType = boxType.replace("full", "fully");
    let tableElem = currentBox.parentElement;
    $('<table style="border:1px solid #A2A9B1; border-radius:2px; padding:10px 16px 0; margin:auto; max-width:55em; width:100%; clear:both;"><tr><td style="color:#808080"><div style="font-style:italic; margin-left:1em;">' + pageWatchers + '</div><div>Quick options:</div></td></tr><tr style="display: flex; justify-content: center;"><td></td></tr><tr><td style="color:#808080">Custom response:</td></tr><tr style="text-align:center;"><td></td></tr><tr style="background:#F6F6F6;"><td style="display:none;"><div style="color:#808080">Preview:</div><div></div></td></tr><tr style="display: flex; justify-content: right;"><td></td></tr></table>').insertAfter(tableElem);
    let responseTable = tableElem.nextElementSibling.children[0];
    let firstRow = responseTable.children[1].children[0];
    let secondRow = responseTable.children[3].children[0];
    let thirdRow = responseTable.children[4].children[0];
    let fourthRow = responseTable.children[5].children[0];
    let protections = Object.entries(dataERT.protections);
    let responses = Object.entries(dataERT.response);
    let quickResponses = Object.entries(dataERT.quickResponse);
    let nonResponses = dataERT.nonResponse;
    //Create type change dropdown
    let tcOptions = [];
    for (let i = 0; i < protections.length; i++) {
        tcOptions.push({data: protections[i][0], label: protections[i][1][0]});
    }
    let typeChange = new OO.ui.DropdownInputWidget({
        value: boxType,
        options: tcOptions
    });
    typeChange.on("change", function () {
        submitB.setDisabled(false);
    });
    typeChange.$element[0].style = "text-align:left; margin:auto";
    $(secondRow).append(typeChange.$element);
    //Create target page list
    let boxLinks = currentBox.getElementsByClassName("mbox-text")[0].getElementsByClassName("external text");
    let pageLinks = [];
    if (boxLinks.length > 0) {//Open request
        for (let c1 = 0; c1 < boxLinks.length; c1++) {
            if (boxLinks[c1].parentElement.tagName == "LI" || boxLinks[c1].parentElement.tagName == "B") {
                pageLinks[pageLinks.length] = boxLinks[c1].innerHTML;
            }
        }
    } else {//Closed request
        boxLinks = currentBox.getElementsByClassName("mbox-text")[0].getElementsByTagName("A");
        for (let c2 = 1; c2 < boxLinks.length; c2++) {
            pageLinks[pageLinks.length] = boxLinks[c2].title;
        }
        if (pageLinks.length == 0) {
            pageLinks = mw.config.get("wgPageName").replace(/(_talk|Talk:)/,"");
        }
    }
    let targetPages = new OO.ui.TagMultiselectWidget({
        placeholder: 'Target Pages',
        allowArbitrary: true,
        selected: pageLinks
    });
    targetPages.on("change", function () {
        submitB.setDisabled(false);
    });
    targetPages.$element[0].style = "text-align:left; margin:5px auto";
    $(secondRow).append(targetPages.$element);
    //Create dropdown menu
    let dropMenu = new OO.ui.DropdownWidget({
        label: "Select reply option - Add additional text below",
        menu: {items: []}
    });
    for (let count = 0; count < responses.length; count++) {
        let newOption = new OO.ui.MenuOptionWidget({
            label: responses[count][0],
            icon: responses[count][1][1]
        });
        dropMenu.menu.addItems([newOption]);
    }
    responses = dataERT.response;
    dropMenu.$element[0].style = "text-align:left; margin:0px";
    $(secondRow).append(dropMenu.$element);
    dropMenu.on("labelChange", function () {
        submitB.setDisabled(false);
        showOutput(inputText, responses[dropMenu.getLabel()], thirdRow, typeChange.value);
    });
    //Create input box
    let inputText = new OO.ui.MultilineTextInputWidget({autosize: true, rows: 4, label: "Additional text"});
    inputText.$element[0].style = "margin:5px auto";
    $(secondRow).append(inputText.$element);
    inputText.on("change", function (newText) {
        showOutput(inputText, responses[dropMenu.getLabel()], thirdRow, typeChange.value);
    });
    //Create top horizontal layout
    let hzLayoutT = new OO.ui.HorizontalLayout();
    //Create firstrow fieldset
    let fieldsetT = new OO.ui.FieldsetLayout();
    fieldsetT.addItems([new OO.ui.FieldLayout(new OO.ui.Widget({content: [hzLayoutT]}), {align: 'top'})]);
    $(firstRow).append(fieldsetT.$element);
    //Remove button
    let remove = new OO.ui.ButtonWidget({
        icon: "trash",
        flags: ["primary", "destructive"],
        invisibleLabel: true,
        title: "Remove the section!"
    });
    remove.on("click", function () {
        secondRow.style = "display:none";
        thirdRow.style = "display:none";
        fourthRow.style = "display:none";
        responseTable.children[2].children[0].style = "display:none";
        submitB.$element.remove();
        hzLayoutT.clearItems();
        //Create deletion options
        let remSec = new OO.ui.ButtonWidget({//RemoveSection
            icon: "trash",
            flags: ["primary", "destructive"],
            label: "Remove section",
            title: "Remove the entire section!"
        });
        remSec.on("click", function () {
            execute(responseTable, nonResponses.Remove, "", null, typeChange.defaultValue, targetPages.getValue(), "nochange", "");//temp
            /*if (toggleESN.selected) {
                execute(responseTable, nonResponses.Remove, "EmptyNotice", null, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
            } else {
                execute(responseTable, nonResponses.Remove, "", null, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
            }*/
        });
        hzLayoutT.addItems([remSec]);
        /*let toggleESN = new OO.ui.CheckboxInputWidget({selected: false});
        hzLayoutT.addItems([toggleESN, new OO.ui.LabelWidget({label: "Give the user an empty section notice"})]);*/
        hzLayoutT.addItems([cancelB]);
    });
    hzLayoutT.addItems([remove]);
    //Open & Close button
    if (currentBox.parentElement.attributes[2].localName != "data-origlevel") {
        let closeB = new OO.ui.ButtonWidget({
            icon: "unFlag",
            invisibleLabel: true,
            title: "Mark as answered"
        });
        closeB.on("click", function () {
            execute(responseTable, nonResponses.Close, "", true, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
        });
        hzLayoutT.addItems([closeB]);
    } else {
        let openB = new OO.ui.ButtonWidget({
            icon: "flag",
            invisibleLabel: true,
            title: "Mark as unanswered"
        });
        openB.on("click", function () {
            execute(responseTable, nonResponses.Open, "", false, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
        });
        hzLayoutT.addItems([openB]);
    }
    //Create quick response buttons
    for (let i = 0; i < quickResponses.length; i++) {
        let newButton = new OO.ui.ButtonWidget({
            label: quickResponses[i][1][0],
            flags: quickResponses[i][1][1],
            title: quickResponses[i][1][2]
        });
        newButton.on("click", function () {
            execute(responseTable, responses[quickResponses[i][0]], "", toggleAns.selected, typeChange.defaultValue, targetPages.getValue(), "nochange", "");
        });
        hzLayoutT.addItems([newButton]);
    }
    //Toggle answer button
    let toggleAns = new OO.ui.CheckboxInputWidget({selected: true});
    hzLayoutT.addItems([toggleAns, new OO.ui.LabelWidget({label: "Answered"})]);
    //Create lastrow horizontal layout
    let hzLayoutB = new OO.ui.HorizontalLayout();
    //Create lastrow fieldset
    let fieldsetB = new OO.ui.FieldsetLayout();
    fieldsetB.addItems([new OO.ui.FieldLayout(new OO.ui.Widget({content: [hzLayoutB]}), {align: 'top'})]);
    $(fourthRow).append(fieldsetB.$element);
    //Cancel response button
    let cancelB = new OO.ui.ButtonWidget({
        icon: "cancel",
        flags: ["destructive"],
        label: "Cancel",
        framed: false,
        title: "Cancel the response & close menu"
    });
    cancelB.on("click", function () {
        replyButton.setDisabled(false);
        responseTable.parentElement.remove();
    });
    hzLayoutB.addItems([cancelB]);
    let isWatched = (typeof (watchStatus[0]) != "undefined");
    //Watch list//Toggle answer button
    let toggleWL = new OO.ui.CheckboxInputWidget({selected: isWatched});
    let toggleWLLable = new OO.ui.LabelWidget({label: "Watch this page"});
    toggleWL.on("change", function (newStatus) {
        watchDropdown.setDisabled(!newStatus);
    });
    toggleWLLable.$element[0].style = "white-space: nowrap;";
    hzLayoutB.addItems([toggleWL, toggleWLLable]);
    let watchValue = "never";
    let watchOptions = [{data: "never", label: "Permanent"}, {data: "1 day", label: "1 day"}, {
        data: "3 days",
        label: "3 days"
    }, {data: "1 week", label: "1 week"}, {data: "1 month", label: "1 month"}];
    let wlExpiry = watchStatus[1];
    if (typeof (wlExpiry) != "undefined") {
        let daysDif = Math.ceil((new Date(wlExpiry).getTime() - Date.now()) / 1000 / 60 / 60 / 24);
        watchOptions.unshift({data: "nochange", label: daysDif + " days"});
        watchValue = wlExpiry;
    }
    //Create WLDropdown horizontal layout
    let hzLayoutWLD = new OO.ui.HorizontalLayout();
    hzLayoutB.addItems([hzLayoutWLD]);
    //Watchlist dropdown
    let watchDropdown = new OO.ui.DropdownInputWidget({
        value: watchValue,
        options: watchOptions,
        disabled: !isWatched
    });
    hzLayoutWLD.addItems([watchDropdown]);
    //Submit response button
    let submitB = new OO.ui.ButtonWidget({
        icon: "checkAll",
        flags: ["primary", "progressive"],
        label: "Submit",
        title: "Submit the response",
        disabled: true
    });
    submitB.on("click", function () {
        let newResponse = responses[dropMenu.getLabel()];
        let newText = inputText.value;
        let isAns = toggleAns.selected;
        let newType = typeChange.value;
        let newTargets = targetPages.getValue();
        let targets = targetPages.items;
        let targetChange = false;
        let wlChange = toggleWL.selected;
        let wlVals = watchDropdown.value;
        if (wlChange) {
            wlChange = "watch";
            if (wlVals == "nochange") {
                wlChange = "nochange";
            }
        } else {
            wlChange = "unwatch";
        }
        if (typeof (newResponse) != "undefined") {
            execute(responseTable, newResponse, newText, isAns, newType, newTargets, wlChange, wlVals);
        } else if (typeChange.value != typeChange.defaultValue) {
            execute(responseTable, nonResponses.ChangeLevel, "", isAns, newType, newTargets, wlChange, wlVals);
        }
        if (targets.length == pageLinks.length) {
            for (let item = 0; item < targets.length; item++) {
                if (targets[item].data != pageLinks[item]) {
                    targetChange = true;
                }
            }
        } else {
            targetChange = true;
        }
        if (targetChange) {
            execute(responseTable, nonResponses.ChangeTarget, "", isAns, newType, newTargets, wlChange, wlVals);
        }
    });
    hzLayoutB.addItems([submitB]);
}

function showOutput(inputText, replyOption, tableRow, template) {
    var restTransform = "https://en.wikipedia.org/api/rest_v1/transform/wikitext/to/html/" + encodePageName;
    let preview = "";
    let newText = inputText.value;
    if (typeof (inputText) == 'string') {
        newText = inputText;
    }
    template = dataERT.protections[template][1];
    if (typeof (replyOption) != "undefined") {
        preview += "{{" + template + replyOption[0] + "}} ";
    }
    if (newText != "" && typeof (newText) != "undefined") {
        preview += newText + " ";
    }
    if (preview != "") {
        let nickname = " " + mw.user.options.values.nickname;
        if (nickname == " ") {//Create default signature if no nickname
            nickname = mw.user.getName();
            nickname = " [[User:" + nickname + "|" + nickname + "]] ([[User talk:" + nickname + "|talk]])";
        }
        let dateObj = new Date();
        let dateNow = dateObj.toLocaleDateString('en-GB', {
            timeZone: 'UTC',
            year: 'numeric',
            month: 'long',
            day: 'numeric'
        });
        let timeNow = dateObj.toLocaleTimeString('en-GB', {timeZone: 'UTC', hour: '2-digit', minute: '2-digit'});
        preview += nickname + " " + timeNow + ", " + dateNow + " (UTC)";
        preview = preview.replaceAll(/{{subst:/gi, "{{");
        $.post(restTransform, 'wikitext=' + encodeURIComponent(preview) + '&body_only=true',
            function (html) {
                if (inputText.value != "" || typeof (replyOption) != "undefined") {//Stops preview appearing with empty input box
                    tableRow.style = "padding:8px 1em 2px;";
                    tableRow.children[1].innerHTML = html;
                }
            }
        );
    } else {
        tableRow.style = "display:none;";
    }
}

if (editRequestBoxes.length != 0) {
    let pageID = mw.config.values.wgArticleId;
    api.get({
        action: "query",
        prop: "info",
        pageids: pageID,
        inprop: "watchers|visitingwatchers|watched",
        format: "json"
    }).done(function (data) {
        data = data.query.pages[pageID];
        let watchers = data.watchers;
        let visiting = data.visitingwatchers;
        watchStatus = [data.watched, data.watchlistexpiry];
        if (typeof (watchers) == "undefined") {
            pageWatchers += "less than 30";
        } else {
            pageWatchers += watchers;
        }
        pageWatchers += " users watching this page (";
        if (typeof (visiting) == "undefined") {
            pageWatchers += "0";
        } else {
            pageWatchers += visiting;
        }
        pageWatchers += " have viewed recent edits).";
    });
    var jsonERTURL = "https://en.wikipedia.org/w/index.php?title=User:Terasail/Edit_Request_Tool.json&action=raw&ctype=text/json";
    $.getJSON(jsonERTURL, function (newData) {
        dataERT = newData;
        mw.loader.load(["oojs-ui-core", "oojs-ui-widgets", "oojs-ui-windows"]);
        mw.loader.load(["oojs-ui.styles.icons-interactions", "oojs-ui.styles.icons-moderation", "oojs-ui.styles.icons-user", "oojs-ui.styles.icons-content", "oojs-ui.styles.icons-editing-core", "oojs-ui.styles.icons-editing-advanced"]);
    });
    for (let i = 0; i < editRequestBoxes.length; i++) {
        let currentBox = editRequestBoxes[i].children[0]; //The tbody tag for the box
        if (typeof (currentBox.parentElement.dataset.origlevel) != "undefined") {
            let isSmall = false;
            if (editRequestBoxes[i].id == "") {
                isSmall = true;
            }
            let replyButton = new OO.ui.ButtonWidget({
                icon: "edit",
                flags: ["progressive"],
                label: "Respond",
                invisibleLabel: isSmall,
                title: "Respond to the edit request."
            });
            replyButton.on("click", function () {
                addButtons(currentBox, replyButton);
                replyButton.setDisabled(true);
            });
            replyButton.$element[0].style = "margin:2px 0";
            if (isSmall) {
                $(currentBox.children[0].children[0]).append(replyButton.$element);
            } else {
                $(currentBox).append('<tr><td colspan=2><div style="display: flex; justify-content: center;"></div></td></tr>');
                $(currentBox.children[1].children[0].children[0]).append(replyButton.$element);
            }
        }
    }
}
//</nowiki>[[Category:Wikipedia scripts]]