User:Joritochip/global.js

importArticles({   type: 'script',    articles: [        'u:dev:AdminDashboard JS-Button/code.js',        'u:dev:MediaWiki:AllPagesHideRedirect/code.js',        'u:dev:MediaWiki:AdminDashboard block/code.js',        'u:dev:MediaWiki:CopyCodeButton.js',        'u:dev:MediaWiki:GlobalEditcount/code.js',        'u:dev:MediaWiki:EditConflictAlert/code.js',        'u:dev:MediaWiki:LakeLinks.js',        'u:dev:MediaWiki:NavButtonsDropdown.js',        'u:dev:MediaWiki:EditBio/code.js',        'u:dev:MediaWiki:MarkForDeletion/code.js',        'u:dev:MediaWiki:LinkThumb.js',        'u:dev:MediaWiki:AdvancedOasisUI/code.js',        'u:dev:MediaWiki:DedicatedTalkButton.js',        'u:dev:MediaWiki:PageCreator/code2.js',        'u:dev:MediaWiki:AddArticleToCategory/code.js',    ] });

require(["jquery", "mw", "wikia.window", "wikia.ui.factory"],       function (jQuery, mw, wk, ui) {    "use strict";

if (jQuery("#massEdit-li").exists || window.isMassEditLoaded) { return; }   window.isMassEditLoaded = true;

if (!window.dev || !window.dev.i18n) { wk.importArticle({           type: "script",            article: "u:dev:MediaWiki:I18n-js/code.js"        }); }

// Constant for future testing const DEBUG = false;

//I18n-js var $i18n;

/**    * @class MassEdit * @classdesc The central MassEdit class */   var MassEdit = { meta: { author: "User:Eizen", created: "05/02/17", lastEdit: "03/09/18", version: "2.4.4" },       hasRights: true legalChars: new RegExp("^[" + wk.wgLegalTitleChars + "]*$"),

/**        * @method addLogEntry * @description Method allows for quick adding of a MassEdit log entry *             to the appropriate text field. * @param {String} $key - The name of the JSON field * @returns {void} */       addLogEntry: function ($key) { jQuery("#massEdit-log").prepend($i18n.msg($key).escape + " "); },

/**        * @method addLogEntry * @description "Overloaded" two-parameter variation of the method above *             that accepts a JSON key and an entry substitute for $1 *             or $2. * @param {String} $key - The name of the JSON field * @param {String} $entry - Name of the page to substitute * @returns {void} */       addLogEntry: function ($key, $entry) { jQuery("#massEdit-log").prepend(               $i18n.msg($key, $entry).escape + " "); },

/**        * @method constructItem * @description Method returns a completed *             representing the menu link item. Is comprised of a        *              link inside a list item. * @param {String} $text - Text to be displayed in the item and title * @returns {String} */       constructItem: function ($text) { return mw.html.element("li", {               "class": "overflow",                "id": "massEdit-li"            }, new mw.html.Raw( mw.html.element("a", {                   "id": "massEdit-a",                    "href": "#",                    "title": $text                }, $text) ));       },

/**        * @method isLegalPage * @description Utility function used to test if inputted page name *             matches the legal characters regex. Returns a boolean *             flag depending on result. * @param {String} $page - Inputted page name * @returns {boolean} */       isLegalPage: function ($page) { if (!this.legalChars.test($page)) { jQuery("#massEdit-modal-form")[0].reset; this.addLogEntry("modalSecurity"); return false; } else { return true; }       },

/**        * @method displayModal * @description Method constructs and displays the main user interface. *             Injects custom CSS prior to construction and handles all *             button click events. * @param {String} $modalHTML - The modal HTML layout * @returns {void} */       displayModal: function ($modalHTML) { var that = this;

mw.util.addCSS(               ".massEdit-menu," +                ".massEdit-textbox {" +                    "width: 100%;" +                    "padding: 0;" +                "}" +                ".massEdit-textarea {" +                    "height: 50px;" +                    "width: 100%;" +                    "padding: 0;" +                    "overflow: auto;" +                "}" +                "#massEdit-log {" +                    "height: 55px;" +                    "width: 100%;" +                    "border: 1px solid;" +                    "font-family: monospace;" +                    "background: #fff;" +                    "color: #aeaeae;" +                    "overflow: auto;" +                    "padding:0;" +                "}"            );

ui.init(["modal"]).then(function (modal) {               var config = {                    vars: {                        id: "massEdit-modal",                        size: "medium",                        title: $i18n.msg("itemTitle").escape,                        content: $modalHTML,                        buttons: [{                            vars: {                                value: $i18n.msg("buttonCancel").escape,                                classes: ["normal", "primary"],                                data: [{                                    key: "event",                                    value: "cancel"                                }]                            }                        }, {                            vars: {                                value: $i18n.msg("buttonClear").escape,                                classes: ["normal", "primary"], data: [{ key: "event", value: "clear" }]                           }                        }, {                            vars: { value: $i18n.msg("buttonSubmit").escape, classes: ["normal", "primary"], data: [{ key: "event", value: "submit" }]                           }                        }]                    }                };

modal.createComponent(config, function (massEditModal) {                   massEditModal.bind("cancel", function  { massEditModal.trigger("close"); });

massEditModal.bind("clear", function {                        jQuery("#massEdit-modal-form")[0].reset;                    });

massEditModal.bind("submit", function {                        that.main;                    });

massEditModal.show; });           });

// Disable replacements menu depending on current selected option jQuery(document).on("change", "#massEdit-actionType", function {                if (jQuery("#massEdit-actionType").val === "replace") {                    jQuery("#massEdit-replaceThis-value")                        .prop("disabled", false);                } else {                    jQuery("#massEdit-replaceThis-value")                        .prop("disabled", true);                }            }); },

/**        * @method getContent * @description This method retrieves the content of the inputted page, *             including information about its time of creation and *             relevant timestamp info. Used exclusively by the find *             and dropdown option. * @param {String} $action - Editing action (prepend, append, replace) * @param {String} $editSummary - Edit summary * @param {String} $page - The page in question * @param {String} $newContent - New text to replace the target * @param {String} $replace - The target to be replaced in the callback * @param {function} callback - The callback handler */       getContent: function ($action, $editSummary, $page, $newContent,                $replace, callback) {

var that = this; this.api.get({               action: "query",                prop: "info|revisions",                intoken: "edit",                titles: $page,                rvprop: "content|timestamp",                rvlimit: "1",                indexpageids: "true",                format : "json"            }).done(function ($data) {                if (!$data.error) {                    callback(           // handleContent that,          // this $action,       // "replace" $editSummary,  // "#massEdit-summary-value" $data,         // $data $page,         // $pagesArray[$counter] $newContent,   // "#massEdit-content-value" $replace       // "#massEdit-replaceThis-value" );               }            });        },

/**        * @method handleContent * @description Callback function for. Sifts *             through included data and passes relevant bits to the *               method. Used exclusively by the *             find and replace dropdown option. * @param {this} that - Scope variable * @param {String} $action - Editing action (prepend, append, replace) * @param {String} $editSummary - Edit summary * @param {JSON} $data - Passed data from * @param {String} $page - Specific page in question * @param {String} $newContent - New text to replace the target * @param {String} $replaceThis - Text to be replaced by $newContent * @returns {void} */       handleContent: function (that, $action, $editSummary, $data, $page,                $newContent, $replaceThis) {

// Check if page actually exists if (Object.keys($data.query.pages)[0] === "-1") { jQuery("#massEdit-modal-form")[0].reset; that.addLogEntry("noSuchPage", $page); return; }

var $newText; var $result = $data.query.pages[Object.keys($data.query.pages)[0]]; var $text = $result.revisions[0]["*"]; var $timestamp = $result.revisions[0].timestamp; var $starttimestamp = $result.starttimestamp; var $token = $result.edittoken;

// Replace all instances of chosen text with inputted new text $newText = $text.split($replaceThis).join($newContent);

// Check if old & new revisions are identical in content if ($newText === $text) { jQuery("#massEdit-log").prepend(                   $i18n.msg("noMatch", $replaceThis, $page).escape +                    " "                ); } else { that.editPage(that, $page, $newText, $action, $editSummary,                   $timestamp, $starttimestamp, $token); }       },

/**        * @method editPage * @description The one-size-fits-all editing handler for use by all *             three main MassEdit functions. Takes several different *             numbers of input parameters depending on the action to         *              be taken by the handler. * @param {this} that - Scope variable * @param {String} $page - The page to be edited * @param {String} $content - The content to be added to the page * @param {String} $action - Editing action (prepend, append, replace) * @param {String} $editSummary - Edit summary * @param {String} $timestamp - Optional, for replace option only * @param {String} $starttimestamp - Optional, for replace option only * @param {String} $token - Optional, for replace option only * @returns {void} */       editPage: function (that, $page, $content, $action, $editSummary,                $timestamp, $starttimestamp, $token) {

// Edit without editing; for testing purposes if (DEBUG) { that.addLogEntry("editSuccess", $page); return; }

// Default base properties var $params = { action: "edit", minor: true, bot: true, title: $page, summary: $editSummary };

// Set additional Object properties depending on action to be taken switch ($action) { case "prepend": $params.prependtext = $content; $params.token = mw.user.tokens.get("editToken"); break; case "append": $params.appendtext = $content; $params.token = mw.user.tokens.get("editToken"); break; case "replace": $params.text = $content; $params.basetimestamp = $timestamp; $params.startimestamp = $starttimestamp; $params.token = $token; break; }

that.api.post($params).done(function ($data) {               jQuery("#massEdit-modal-form")[0].reset;                if (!$data.error) {                    that.addLogEntry("editSuccess", $page);                } else {                    that.addLogEntry("editFailure", $page);                }            }); },

/**        * @method getNamespaceMembers * @description As the name implies, this method returns the results of        *              a request for an inputted namespace's associated page *             contents. * @param {String} $namespace - Number of the namespace in question * @return {JSON} */       getNamespaceMembers: function ($namespace) { return jQuery.ajax({               type: "GET",                url: mw.util.wikiScript("api"),                data: {                    action: "query",                    list: "allpages",                    aplimit: "max",                    apnamespace: $namespace,                    format: "json"                }            }); },

/**        * @method getCategoryMembers * @description As the name implies, this method returns the results of        *              a request for an inputted category's associated page *             contents. * @param {String} $category - Name of the category in question * @return {JSON} */       getCategoryMembers: function ($category) { return jQuery.ajax({               type: "GET",                url: mw.util.wikiScript("api"),                data: {                    action: "query",                    list: "categorymembers",                    cmtitle: $category,                    cmprop: "title",                    cmdir: "desc",                    cmlimit: "max",                    format: "json"                }            }); },

/**        * @method membersHandler * @description This method, inspired by Java-style reflection, is a        *              general purpose handler for use by both namespace and *             category bulk editing. The method accepts either the *             getCategoryMembers or         *              getNamespaceMembers methods as the requests of         *              choice, running basically the same process with the *             results of each once all calls have been completed. *

*

*             Basically, once the calls have been made, the method *             assembles all the pages from the request results and *             pushes them into an array for processing by the *             actionHandler method. * @param {String[]} $inputArray * @param {String} $newContent * @param {String} $toReplace * @param {int} $actionIndex * @param {String} $action * @param {String} $editSummary * @param {function} getMembers * @param {String} $queryProperty * @returns {void} */       membersHandler: function ($inputArray, $newContent, $toReplace,                $actionIndex, $action, $editSummary, getMembers,                $queryProperty) {

var that = this; var $members; var $requests; var $arguments; var $defer; var $data;

$members = []; $requests = []; $arguments = [];

$inputArray.forEach(function ($member) {               if ( $queryProperty === "categorymembers" && !$member.startsWith("Category:") ) {                   $member = "Category:" + $member;                }

if (                   ( // If is category-based and member is legal $queryProperty === "categorymembers" && that.isLegalPage($member) ) ||                   ( // If is namespace-based and member is integer $queryProperty === "allpages" && $member.match(/^[0-9]+$/) !== null )               ) {                    $members.push($member); $requests.push(getMembers($member)); }           });

if (               !$members.length ||                !$requests.length ||                $members.length !== $requests.length            ) { return; }

$inputArray = []; $defer = jQuery.when.apply(jQuery, $requests);

$defer.done(function {                if ($requests.length === 1) {                    $arguments.push(arguments);                } else {                    $arguments = Array.prototype.slice.call(arguments);                }

jQuery.each($arguments, function ($index, $results) {                   if ($results[1] === "success") {                        $data = $results[0].query[$queryProperty];

if ($data === undefined || $data.length === 0) { that.addLogEntry("noSuchPage", $members[$index]); return; }

$data.forEach(function ($page) {                           $inputArray.push($page.title);                        }); }               });

if ($inputArray.length) { that.actionHandler($inputArray, $newContent, $toReplace,                       $actionIndex, $action, $editSummary); }           });        },

/**        * @method actionHandler * @description This function invokes methods based on user's desired *             action. If the user is not in the proper user rights *             group, access is denied. If no action is selected, the *             user is prompted to select an action. *             setInterval is employed to ensure that the *             script does not make too many consecutive content GETs *             or edit POSTs; replaces forEach *             implementation. *

*

*             Addition of global pages-based find-and-replace option *             replaces old find-and-delete option, and now checks for *             cases of empty pages or empty target text. * @param {String[]} $inputArray * @param {String} $newContent * @param {String} $toReplace * @param {int} $actionIndex * @param {String} $action * @param {String} $editSummary * @returns {void} */       actionHandler: function ($inputArray, $newContent, $toReplace,                $actionIndex, $action, $editSummary) {

var that = this; var $counter = 0; var $editInterval;

switch ($actionIndex) { case 0: // No action selected this.addLogEntry("noOptionSelected"); break; case 1: case 2: // Edit methods (prepend and append) this.addLogEntry("loading");

$editInterval = setInterval(function {                    if (that.isLegalPage($inputArray[$counter])) {                        that.editPage(that, $inputArray[$counter], $newContent, $action, $editSummary);                   }                    $counter++;                    if ($counter === $inputArray.length) {                        clearInterval($editInterval);                    }                }, that.config.editInterval); break; case 3: // Find and replace this.addLogEntry("loading");

$editInterval = setInterval(function {                    if (that.isLegalPage($inputArray[$counter])) {                        that.getContent($action, $editSummary, $inputArray[$counter], $newContent, $toReplace, that.handleContent);                   }

$counter++; if ($counter === $inputArray.length) { clearInterval($editInterval); }               }, that.config.editInterval);                break;            }        },

/**        * @method main * @description The main method handles the collection of user input *             from the GUI modal and tests against a series of base *             cases to ensure ill-formed or illegitimate input is not *             included in the program's operations. Furthermore, the *             new category-based function necessitated the removal of         *              some old code used to execute different actions (i.e.         *              prepend, append, etc.) to a different method, namely *             actionHandler. The main method now handles the *             acquisition of category members from user input cats, *             passing the array of pages to the handler above. *

*

*             Addition of global pages-based find-and-replace option *             replaces old find-and-delete option, and now checks for *             cases of empty pages or empty target text. * @returns {void} */       main: function  { var that = this;

// Values of textareas var $newContent = jQuery("#massEdit-content-value")[0].value; var $toReplace = jQuery("#massEdit-replaceThis-value")[0].value; var $pagesInput = jQuery("#massEdit-pages-value")[0].value; var $editSummary = jQuery("#massEdit-summary-value")[0].value; var $pagesArray = $pagesInput.split(/[\n]+/);

// Dropdown menu var $actionIndex = jQuery("#massEdit-actionType")[0].selectedIndex; var $action = jQuery("#massEdit-actionType").val; var $typeIndex = jQuery("#massEdit-contentType")[0].selectedIndex;

// Is not in the proper rights group if (!this.hasRights) { jQuery("#massEdit-modal-form")[0].reset; this.addLogEntry("modalUserRights"); return;

// No pages included } else if (!$pagesInput) { this.addLogEntry("noPages"); return;

// Is either append/prepend with no content input included } else if ($action !== "replace" && !$newContent) { this.addLogEntry("noContent"); return;

// Is find-and-replace with no target content included } else if ($action === "replace" && !$toReplace) { this.addLogEntry("noTarget"); return;

// If user forgot to select dropdown options (no reset b/c annoying) } else if ($actionIndex === 0 || $typeIndex === 0) { this.addLogEntry("noOptionSelected"); return;

// If edit summary is greater than permitted max of 800 characters } else if ($editSummary.length > 800) { this.addLogEntry("overlongSummary"); return; }

switch ($typeIndex) { case 1: // Loose pages that.actionHandler($pagesArray, $newContent, $toReplace,                   $actionIndex, $action, $editSummary); break; case 2: // Categories that.membersHandler($pagesArray, $newContent, $toReplace,                   $actionIndex, $action, $editSummary,                    that.getCategoryMembers, "categorymembers"); break; case 3: // Namespaces that.membersHandler($pagesArray, $newContent, $toReplace,                   $actionIndex, $action, $editSummary,                    that.getNamespaceMembers, "allpages"); break; }       },

/**        * @method init * @description Method initializes the program, assembling the toolbar *             link and handling click events. Config options are set *             per user input or the defaults. * @param {JSON} $lang - I18n-js content * @returns {void} */       init: function ($lang) { var that = this;

$i18n = $lang; $i18n.useContentLang;

this.api = new mw.Api; this.config = jQuery.extend({               editInterval: 1500            }, window.massEditConfig);

$i18n.useUserLang;

/**            * Handle removal of   right for bots/users * Current rate limit for default user/sysop/etc. is 40 edits/minute * Current rate limit for bot (not bot-global) is 80 edits/minute * @see SUS-4775 * @see VariablesBase.php */           if (                jQuery.inArray("bot", wk.wgUserGroups) !== -1 &&                this.config.editInterval < 750            ) { this.config.editInterval = 750; // Reset to max 80 edits/minute } else if (               jQuery.inArray("user", wk.wgUserGroups) !== -1 &&                this.config.editInterval < 1500            ) { this.config.editInterval = 1500; // Reset to max 40 edits/minute }

var $modalHTML = "<form id='massEdit-modal-form' class='WikiaForm '>" + " " +                   " " + $i18n.msg("modalSelect").escape + " " +                       "<select size='1' id='massEdit-actionType'" + "class='massEdit-menu' name='action'>" + "<option selected=''>" + $i18n.msg("modalSelect").escape + " " +                           "<option value='prepend'>" + $i18n.msg("dropdownPrepend").escape + " " +                           "<option value='append'>" + $i18n.msg("dropdownAppend").escape + " " +                           "<option value='replace'>" + $i18n.msg("dropdownReplace").escape + " " +                       " " +                        " " +                    " " +                    " " +                    " " + $i18n.msg("modalContentType").escape + " " +                       "<select size='1' id='massEdit-contentType'" + "class='massEdit-menu' name='action'>" + "<option selected=''>" + $i18n.msg("modalContentType").escape + " " +                           "<option value='pages'>" + $i18n.msg("dropdownPages").escape + " " +                           "<option value='categories'>" + $i18n.msg("dropdownCategories").escape + " " +                           "<option value='namespaces'>" + $i18n.msg("dropdownNamespaces").escape + " " +                       " " +                        " " +                    " " +                    " " +                    " " + $i18n.msg("modalContentTitle").escape + " " +                       "<textarea id='massEdit-content-value' " + "class='massEdit-textarea' placeholder='" + $i18n.msg("modalContentPlaceholder").escape + "'/>" +                       " " +                    " " +                    " " +                    " " + $i18n.msg("modalReplaceTitle").escape + " " +                       "<textarea id='massEdit-replaceThis-value' " + "class='massEdit-textarea' placeholder='" + $i18n.msg("modalReplacePlaceholder").escape + "' disabled/>" + " " +                   " " +                    " " +                    " " + $i18n.msg("modalPagesTitle").escape + " " +                       "<textarea id='massEdit-pages-value' " + "class='massEdit-textarea' placeholder='" + $i18n.msg("modalPagesPlaceholder").escape + "'/>" +                       " " +                    " " +                    " " +                    " " + $i18n.msg("modalSummaryTitle").escape + " " +                       "<input type='textbox' id='massEdit-summary-value'" + "class='massEdit-textbox' />" + " " +                   " " +                " " +                " " +                " " +            " " +            " " + $i18n.msg("modalLog").escape + " " + " " +           " ";

var $tbElement = this.constructItem($i18n.msg("itemTitle").plain); jQuery($tbElement).prependTo("#my-tools-menu").click(function {                that.displayModal($modalHTML);            }); }   };

mw.hook("dev.i18n").add(function ($i18n) {       jQuery.when( $i18n.loadMessages("MassEdit"), mw.loader.using(["mediawiki.util", "mediawiki.api"]) ).done(jQuery.proxy(MassEdit.init, MassEdit));   }); });