User:Nihiltres/nothingthree.js

/* This nothingthree library is a collection of functions that are probably useful. They're created or collected (as noted) by Nihiltres for his use on Wikipedia, and you can use them as you see fit. There is no guarantee that they work; Nihiltres only tests them under Safari on Mac OS, though will occasionally correct or minimize bugs that occur on other browsers. Enjoy! Note the importScript activator at the bottom. You'll need a user nothingthree-config.js file as well as importing this. /*global nothingthree: true, importScript, importStylesheetURI, jQuery, mw */ mw.loader.using(["mediawiki.util", "oojs-ui.styles.icons-editing-list"], function {	window.nothingthree = {

settings: { //Holds nothingthree globals and defaults topsHidden: false, //Tops start out shown, so this has to be false to start. pageRCloaded: false, //Are revisions loaded? Has to be false to start. specificAutoWatchNamespaces: [1, 2, 3, 8, 9], //Default auto-watch namespaces monthlist: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], //default names for months, allows for basic i18n dateOrder: mw.user.options.get("date") //Default date order. Iff "mdy", nothingthree.util.stringifyDate's output changes slightly. }, //end settings

util: { linkFix: function (targetId, replacementText) { // Tab name fixer; drills down to bottom first-child of targeted ID, then replaces its text. var drilldown = jQuery("#".concat(targetId)); while (drilldown.children.length > 0) { drilldown = drilldown.children.first; }				drilldown.text(replacementText); //jQuery.text natively escapes HTML, AFAICT }, //end linkfix isMobile: function { //really rather basic mobile-device detection here, but can be improved as needed. Mostly useful for tidy code. return !!(navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i) || (navigator.userAgent.match(/Android/i))); }, //end isMobile parseDate: function (dateString) { //Turns the date strings that the MW API likes to use (e.g. "2011-04-06T22:24:52Z") into JS dates. It assumes everything's in UTC, and expects a string for input. var dateParts = /^\s*(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z\s*$/.exec(dateString); if (dateParts) { return new Date(Date.UTC(+dateParts[1], +dateParts[2] - 1, +dateParts[3], +dateParts[4], +dateParts[5], +dateParts[6])); }				return new Date(NaN); //In case dateString isn't what we're expecting… }, //end parseDate stringifyDate: function (theDate) { //Turns a date object into a pretty UTC output string. var dateParts, timeParts; timeParts = ("0" + theDate.getUTCHours).slice(-2) + ":" + ("0" + theDate.getUTCMinutes).slice(-2); if (nothingthree.settings.dateOrder === "mdy") { dateParts = nothingthree.settings.monthlist[theDate.getUTCMonth] + " " + theDate.getUTCDate + ", " + theDate.getUTCFullYear; } else { dateParts = theDate.getUTCDate + " " + nothingthree.settings.monthlist[theDate.getUTCMonth] + " " + theDate.getUTCFullYear; }				return (timeParts + ", " + dateParts + " (UTC)"); }, //end stringifyDate formatInt: function (num) { //Formats integers with commas var numString = num.toString; while (/(\d+)(\d{3})/.test(numString)) { numString = numString.replace(/(\d+)(\d{3})/, "$1,$2"); }				return numString; }, //end formatInt addPortletLink: function (obj) { var $item, $portlet, $list; if (!obj || !obj.portlet || !obj.portlet.jquery || !obj.portlet.length || !obj.text || (!obj.href && !obj.function)) { return null; }				$item = jQuery("").attr("id", (obj.id || null)); $item.addClass("mw-list-item"); $item.children.text(obj.text).attr({					href: (obj.href || "#"),					title: (obj.tooltip || null),					accesskey: (obj.accesskey || null)				}).updateTooltipAccessKeys; $portlet = obj.portlet.first; //if ($portlet.hasClass("vectorTabs")) { $item.find("a").wrapInner(" "); //}				if ($portlet.hasClass("vector-menu-tabs")) { $item.addClass("vector-tab-noicon"); }				$list = $portlet.find("ul").first; if ($list.length === 0) { $list = jQuery(""); if ($portlet.find('div:first').length === 0) { $portlet.append($list); } else { $portlet.find('div').last.append($list); }					if ($portlet.find("ul") === 0) { return null; }				}				$portlet.removeClass("emptyPortlet"); if (obj.next && obj.next.jquery && $list.find(obj.next.first).length !== 0) { $item.insertBefore(obj.next.first); } else if (obj.prev && obj.prev.jquery && $list.find(obj.prev.first).length !== 0) { $item.insertAfter(obj.prev.first); } else { $item.appendTo($list); }				return $item.click((typeof obj.function === "function") ? obj.function : null); } //end addPortletLink }, //end util tabAdd: { //Collection of independent tab-addition routines. log: function { //Adds a link to the log of actions performed by the user. Mainly useful for administrators. if (jQuery("#pt-log").length > 0) { return; }				nothingthree.util.addPortletLink({					portlet: jQuery("#p-personal"),					href: mw.util.getUrl("Special:Log", {user: mw.config.get("wgUserName")}),					text: "Log",					id: "pt-log",					tooltip: "Log of your non-edit actions",					next: jQuery("#pt-mycontris")				}); if (jQuery("#p-personal .mw-ui-icon").length > 0) { jQuery("#pt-log a").prepend($("")) //userContributions is closer to what we want, but to differentiate //the icons a bit, we'll use listBullet }				//jQuery("#pt-log").addClass("collapsible"); not necessary on p-personal links if (mw.config.get("wgPageName") === "Special:Log" && jQuery("#mw-input-user input").val === mw.config.get("wgUserName")) { jQuery("#pt-log").addClass("active"); }			}, //end log sandbox: function { //Used to add a sandbox link; now that that's present by default, this function normalizes the link to reflect the function's old target. jQuery("#pt-sandbox a").first.attr("href", mw.util.getUrl("User:".concat(mw.config.get("wgUserName"), "/Sandbox"))); }, //end sandbox purge: function { //Adds a link to purge the current page. if (jQuery("#ca-purge").length > 0 || mw.config.get("wgCanonicalNamespace") === "Special") { return; }				nothingthree.util.addPortletLink({					portlet: jQuery("#p-cactions"),					href: mw.util.getUrl(mw.config.get("wgPageName"), {"action": "purge"}),					text: "Purge",					id: "ca-purge",					tooltip: "Purge the server cache of this page",					next: jQuery("#p-cactions #ca-unwatch, #p-cactions #ca-watch")				}); } //end purge }, //end tabAdd specificAutoWatch: function { //Automatically checks the "Watch this page" button when editing MediaWiki-namespace pages. if (mw.config.get("wgAction") === "edit" && nothingthree.settings.specificAutoWatchNamespaces.indexOf(mw.config.get("wgNamespaceNumber")) !== -1) { jQuery("#wpWatchthis").prop("checked", true); }		}, //end watchMediawikiSpaceEdits tabMove: { //Tab-moving routines, and custom bits that make use of them. core: function (obj) { //Now slightly less ugly! Does the actual moving part. Takes an object loaded like so: {id: "foo", followingElement: "bar", targetParent: "baz"}. The latter two arguments within that object are optional. var $id, $followingElement, $targetParent; $id = (obj.id ? jQuery("#".concat(obj.id)) : []); $followingElement = (obj.followingElement ? jQuery("#".concat(obj.followingElement)) : []); $targetParent = (obj.targetParent ? jQuery("#".concat(obj.targetParent)) : jQuery((obj.followingElement ? $followingElement.parent : []))); if ($id.length !== 1 || $targetParent.length !== 1) { return; }				if ($id.closest("ul").children.length === 1) { $id.closest("div:not(.menu)").addClass("emptyPortlet"); }				$targetParent.removeClass("emptyPortlet"); //if (!$id.children.first.is("span")) { //	$id.wrapInner(" "); //}				if ($targetParent.hasClass("vector-menu-tabs")) { $id.addClass("vector-tab-noicon"); }				if ($targetParent.attr("id") === "p-views") { //Necessary until better collapsibleTabs support is implemented $id.addClass("collapsible"); }				if ($followingElement.length > 0) { $id.detach.insertBefore($followingElement.first); } else { $id.detach.appendTo($targetParent.find("ul").first); }				//It'd be nice to properly update the collapsibleTabs data RIGHT HERE, but for now, that's not implemented. //jQuery.collapsibleTabs.handleResize; //Force an update regardless }, //end core protection: function { //if the page is protected, show the unprotect button; serves also as an indicator for protection, indicating the value in the title. var restrictionStringArray = []; if (jQuery("#ca-unprotect").length === 0) { //There are more thorough checks, but this seems stable enough… return; }				nothingthree.tabMove.core({"id": "ca-unprotect", "followingElement": "ca-history", "targetParent": "p-views"}); if (mw.config.get("wgRestrictionEdit")) { restrictionStringArray.push("edit: [".concat(mw.config.get("wgRestrictionEdit").join(", "), "]")); }				if (mw.config.get("wgRestrictionMove")) { restrictionStringArray.push("move: [".concat(mw.config.get("wgRestrictionMove").join(", "), "]")); }				if (mw.config.get("wgRestrictionCreate")) { restrictionStringArray.push("create: [".concat(mw.config.get("wgRestrictionCreate").join(", "), "]")); }				jQuery("#ca-unprotect a").attr("title", "Change protection of this page (".concat(restrictionStringArray.join(", "), ")")); }, //end protection deletion: function { //If the page contains a deletion template or a redlinked redirect, or is an empty proposed deletion category, move out the delete tab. In the redirect or category cases, give an automatic, useful deletion summary. var cada = jQuery("#ca-delete a"), moveObj = {id: "ca-delete", followingElement: "ca-history", targetParent: "p-views"}; if (jQuery("[class*=mbox-speedy], [class*=mbox-delete]").length >= 1) { //if there's a deletion template present… nothingthree.tabMove.core(moveObj); }				if ((mw.config.get("wgPageName").indexOf("Category:Proposed_deletion_as_of") !== -1) && (jQuery("#mw-category-empty").length !== 0)) { //if it's an empty proposed deletion category… nothingthree.tabMove.core(moveObj); if (cada.length !== 0) { cada.first.attr("href", cada.first.attr("href").concat("&wpReason=%5B%5BWP%3ACSD%23G6%7CG6%5D%5D%3A%20Empty%20proposed%20deletion%20category")); }				}				if (jQuery(".redirectText a[href*='redlink=1']").length > 0) { //if it's a redlink redirect… nothingthree.tabMove.core(moveObj); if (cada.length !== 0) { cada.first.attr("href", cada.first.attr("href").concat("&wpReason=%5B%5BWP%3ACSD%23G8%7CG8%5D%5D%3A%20Redirect%20to%20a%20deleted%20page")); }				}			}, //end deletion watch: function { //This bit moves the watch tab back into the menu after removing its icon status. jQuery("#ca-watch, #ca-unwatch").removeClass("icon"); nothingthree.tabMove.core({"id": jQuery("#ca-watch, #ca-unwatch").first.attr("id"), "targetParent": "p-cactions"}); }, //end watch contribs: function { var user; if (mw.config.get("wgCanonicalSpecialPageName") == "Contributions") { user = mw.config.get("wgPageName").split("/")[1] || mw.util.getParamValue("target"); nothingthree.util.addPortletLink({						portlet: jQuery("#p-associated-pages"),						href: mw.util.getUrl("User:" + user),						text: "User page",						id: "ca-nstab-user",						tooltip: "View the user page",						next: jQuery("#ca-nstab-special")					}); nothingthree.util.addPortletLink({						portlet: jQuery("#p-associated-pages"),						href: mw.util.getUrl("User_talk:" + user),						text: "Talk",						id: "ca-talk",						tooltip: "Discussion about the content page",						next: jQuery("#ca-nstab-special")					}); nothingthree.util.linkFix("ca-nstab-special", "Contributions"); jQuery("#t-contributions").addClass("selected"); }				if (jQuery("#t-contributions").length === 1) { nothingthree.tabMove.core({id: "t-contributions", targetParent: "p-associated-pages"}); jQuery("#t-contributions").attr("id", "ca-contributions"); nothingthree.util.linkFix("ca-contributions", "Contributions"); }			}		}, //end tabMove sidebar: { //A collection of functions for playing with the sidebar, mostly to collapse or expand it. toggle: function { //Toggles between collapsed and expanded jQuery("#mw-panel, #content, #head-base, #footer, #mw-head-base, #left-navigation").toggleClass("n3-sidebar-collapsed"); if (jQuery.cookie("sideBarCollapsed") === "collapsed") { jQuery.cookie("sideBarCollapsed", "expanded", {path: "/"}); } else { jQuery.cookie("sideBarCollapsed", "collapsed", {path: "/"}); //This isn't error-tolerant enough for my taste, but the likelihood that something else will change this value is low, so I haven't bothered to write more. } //end if			}, //end toggle toggleTab: function { //Adds a tab to the menu to allow the collapsed status to be easily toggled. if (jQuery("#ca-sidebar").length > 0) { return; }				nothingthree.util.addPortletLink({					portlet: jQuery("#p-cactions"),					function: nothingthree.sidebar.toggle,					text: "Toggle sidebar",					id: "ca-sidebar",					tooltip: "Hide or show the sidebar",					next: jQuery("#p-cactions #ca-purge, #p-cactions #ca-unwatch, #p-cactions #ca-watch")				}); if (mw.config.get("wgCanonicalNamespace") === "Special") { nothingthree.tabMove.core({"id": "ca-sidebar", "targetParent": "p-views"}); } //end if			}, //end toggleTab remember: function { //Collapses the sidebar if it was previously collapsed. if (jQuery.cookie("sideBarCollapsed") === "collapsed") { nothingthree.sidebar.toggle; } //end if			} //end remember }, //end sidebar tops: { //A collection of functions designed to allow the visibility of (top) edits on contribution pages to be toggled. Also included are edits where a later edit to the same page is (top). hide: function { //Hides (top) edits. var title, topElsewhere; topElsewhere = []; jQuery(".mw-contributions-list li").each(function {					title = jQuery(this).find(".mw-contributions-title").first.text;					if (jQuery(this).find(".mw-uctop").length > 0) {						topElsewhere.push(title);					}					if (topElsewhere.indexOf(title) !== -1) {						jQuery(this).addClass("n3-tops-faded");					}				}); nothingthree.settings.topsHidden = true; }, //end hide show: function { //Shows (top) edits. jQuery(".n3-tops-faded").removeClass("n3-tops-faded"); nothingthree.settings.topsHidden = false; }, //end show toggle: function { //Toggles between hidden and shown based on the current value. if (nothingthree.settings.topsHidden === true) { nothingthree.tops.show; } else { nothingthree.tops.hide; }			}, //end toggle toggleTab: function { //Adds a tab to the menu allowing the visibility of (top) edits to be toggled. if (jQuery("body").hasClass("mw-special-Contributions") && jQuery("#ca-tops").length === 0) { nothingthree.util.addPortletLink({						portlet: jQuery("#p-views"),						function: nothingthree.tops.toggle,						text: "Toggle (top) entries",						id: "ca-tops",						tooltip: "Hide or show the (top) entries",						next: jQuery("#ca-sidebar")					}); jQuery("#ca-tops").addClass("collapsible"); } //end if			} //end toggleTab }, //end tops monoedit: { toggle: function { if ((mw.config.get("wgAction") === "edit" || mw.config.get("wgAction") === "submit") && jQuery("#wpTextbox1").length === 1) { jQuery("#wpTextbox1").toggleClass("n3-monoedit"); } //end if			}, //end toggle toggleButton: function { if ((mw.config.get("wgAction") === "edit" || mw.config.get("wgAction") === "submit") && jQuery("#wpTextbox1").length === 1) { mw.loader.using("ext.wikiEditor.toolbar", function {						jQuery("#wpTextbox1").wikiEditor("addToToolbar", { //Add a custom group for the button "section": "main", "groups": { "views": {} }						});						jQuery("#wpTextbox1").wikiEditor("addToToolbar", { //add the button itself, to the group "section": "main", "group": "views", "tools": { "monospace": { "label": "Toggle monospace", "type": "button", "icon": "//upload.wikimedia.org/wikipedia/commons/c/c2/Toolbaricon_regular_M.png", //Compromise, no base64 image support AFAICT "action": { "type": "callback", "execute": function { nothingthree.monoedit.toggle; } //end execute } //end action } //end monospace } //end tools }); //end add button						jQuery(".group-views").insertBefore(".section-main .group-format"); //Move the group where I want it.					}); } //end if			} //end toggleButton }, //end monoedit customRevs: { getHistory: function (obj) { //Makes the main AJAX call, retrieving the core data and passing along the callback var historyCall; historyCall = new mw.Api; if (nothingthree.customRevs.gettingHistory) { return; }				nothingthree.customRevs.gettingHistory = true; historyCall.get({ //Eventually this item should have something dangling from it for attaching filters					action: "query",					prop: "revisions",					pageids: mw.config.get("wgArticleId"),					continue: (obj ? obj.continueString || "" : ""),					rvprop: "ids|timestamp|flags|user|parsedcomment|size|tags",					rvdir: "older",					rvlimit: (obj ? obj.limit || 50 : 50),					rvtoken: "rollback"				}) .done(function (data) {						if (data.continue && data.continue.rvcontinue) { //This should probably have "else" code if continuation is impossible							nothingthree.customRevs.historyContinue = data.continue.rvcontinue;						}						nothingthree.customRevs.revisionCleanQueue = data.query.pages[mw.config.get("wgArticleId").toString].revisions;						nothingthree.customRevs.revisionCleanQueue[0].canRollback = true; //This is ugly; I need a better way to resolve rollback-ability						nothingthree.customRevs.resolveRevisions;						nothingthree.customRevs.gettingHistory = false;					}); }, //end getHistory resolveRevisions: function { //Makes any further calls to preprocess the data, then renders the revisions to the page var revsToGet, historyCall, queryMax, cleaningList; revsToGet = []; if (mw.config.get("wgUserGroups").indexOf("sysop") !== -1) { //This isn't good, but fair enough for now… queryMax = 500; } else { queryMax = 50; }				historyCall = new mw.Api; cleaningList = nothingthree.customRevs.revisionCleanQueue.splice(0, Math.max(nothingthree.customRevs.revisionCleanQueue.length, queryMax)); jQuery.each(cleaningList, function {					revsToGet.push(this.parentid);				}); //end jQuery.each historyCall.get({					action: "query",					prop: "revisions",					revids: revsToGet.join("|"),					rvprop: "size"				}) .done(function (data) {						var retrievedRevisions = data.query.pages[mw.config.get("wgArticleId").toString].revisions.reverse;						jQuery.each(cleaningList, function (index) { this.sizediff = (this.size - retrievedRevisions[index].size); this.timestamp = nothingthree.util.parseDate(this.timestamp); if (!this.canRollback) { //Second part of the ugly bit delete this.rollbacktoken; }							nothingthree.customRevs.revisionData.push(this); nothingthree.customRevs.renderHistoryItem(this, jQuery("#n3-pagehistory").first); }); //end jQuery.each						if (nothingthree.customRevs.revisionCleanQueue.length > 0) {							nothingthree.customRevs.resolveRevisions;						} else {							jQuery("input[type=checkbox]:not(.noshiftselect)").checkboxShiftClick; //Reinitialize shift-clicking now that new elements have been created						} //end if/else					}); //end historyCall & done; }, //end getCleanHistory gettingHistory: false, revisionData: [], //Stores cleaned list, starts empty revisionCleanQueue: [], historyContinue: "", //Stores latest rvcontinue; this code needs better structure renderHistoryItem: function (revObject, $historylist) { var renderedItem, byteLabels; //Permanent structures // init THE BIG BLOB renderedItem = jQuery(""); renderedItem.append("      Compare: </ul> <span class=\"n3-rev-actions\"> Actions: <ul></ul> <div class=\"n3-rev-bytediff\">  "); // n3-rev-timestamp renderedItem.find(".n3-rev-timestamp") .attr("href", mw.util.getUrl(mw.config.get("wgPageName"), {oldid: revObject.revid})) .text(nothingthree.util.stringifyDate(revObject.timestamp)); // n3-rev-compares jQuery.each(nothingthree.customRevs.compares, function {					if (typeof this === "function") {						renderedItem.find(".n3-rev-compares ul").append(nothingthree.customRevs.newLia(this(revObject)));					}				}); //n3-rev-comment if (revObject.parsedcomment !== "") { renderedItem.find(".n3-rev-comment").html(revObject.parsedcomment); } else { renderedItem.find(".n3-rev-comment").addClass("n3-rev-nocomment").text("(no edit summary)"); }				//n3-rev-actions jQuery.each(nothingthree.customRevs.actions, function {					if (typeof this === "function") {						renderedItem.find(".n3-rev-actions ul").append(nothingthree.customRevs.newLia(this(revObject)));					}				}); //n3-rev-bytes & n3-rev-bytebar if (revObject.sizediff < 0) { byteLabels = ["n3-rev-bytesremoved", "&minus;"]; } else if (revObject.sizediff > 0) { byteLabels = ["n3-rev-bytesadded", "&plus;"]; } else { byteLabels = ["n3-rev-bytesnull", "&plusmn;"]; }				renderedItem.find(".n3-rev-bytediff").html("<span class=\"".concat(byteLabels[0], "\">", byteLabels[1], nothingthree.util.formatInt(Math.abs(revObject.sizediff)), " &rarr; ", nothingthree.util.formatInt(revObject.size), " bytes")); //Structures which ought to eventually be optional: // n3-rev-form in n3-rev-diff, would be conditional on revision list type; revdel name attr is placeholder renderedItem.find(".n3-rev-diff").prepend("<div class=\"n3-rev-form\"><input name=\"oldid\" type=\"radio\"><input name=\"diff\" type=\"radio\"><input name=\"revdel\" type=\"checkbox\"> "); renderedItem.find("input[name=\"oldid\"], input[name=\"diff\"]").attr("value", revObject.revid); renderedItem.find("input[name=\"revdel\"]").attr("name", "ids[" + revObject.revid.toString + "]"); // n3-rev-user in n3-rev-main, would be conditional on revision list type renderedItem.find(".n3-rev-main").append("<div class=\"n3-rev-user vectorMenu\"><h3 class=\"n3-rev-username\"> <div class=\"n3-rev-userlinks menu\"><ul></ul> "); renderedItem.find(".n3-rev-username").text(revObject.user); jQuery.each(nothingthree.customRevs.userlinks, function {					if (typeof this === "function") {						renderedItem.find(".n3-rev-userlinks ul").append(nothingthree.customRevs.newLia(this(revObject)));					}				}); // n3-rev-flagtags in n3-rev-summary, would be conditional on presence renderedItem.find(".n3-rev-summary > span").append("<span class=\"n3-rev-flagtags\"><ul></ul> "); if (revObject.minor !== undefined) { renderedItem.find(".n3-rev-flagtags ul").append("<li>Minor edit</li>"); }				jQuery.each(revObject.tags, function {					renderedItem.find(".n3-rev-flagtags ul")						.append(nothingthree.customRevs.newLia({text: "Tag", href: mw.util.getUrl("Special:Tags"), title: "Special:Tags"}).append(": ", jQuery("  ").text(this)));				}); //Initialize rendered revision item $historylist.append(renderedItem); $historylist.children.last.find(".n3-rev-user").click(function {					jQuery(this).toggleClass("menuForceShow");				}); }, //end renderHistoryItem newLia: function (obj) { //Simplifies making all of the individual listy items var newItem = jQuery("<li><a></a></li>"); if (!obj) { return null; }				if (obj.text) { newItem.children.text(obj.text); }				if (obj.href) { newItem.children.attr("href", obj.href); }				if (obj.title) { newItem.children.attr("title", obj.title); }				return newItem; }, //end newLia compares: { diff: function (revObject) { return { text: "Previous", href: mw.util.getUrl(mw.config.get("wgPageName"), {diff: revObject.revid, oldid: revObject.parentid}) };				}, //end diff cur: function (revObject) { return { text: "Current", href: mw.util.getUrl(mw.config.get("wgPageName"), {oldid: revObject.revid, diff: "cur"}) };				} //end cur }, //end compares userlinks: { page: function (revObject) { return { text: "User page", href: mw.util.getUrl("User:" + revObject.user) };				}, //end page talk: function (revObject) { return { text: "Talk", href: mw.util.getUrl("User talk:" + revObject.user) };				}, //end talk contribs: function (revObject) { return { text: "Contributions", href: mw.util.getUrl("Special:Contributions/" + revObject.user) };				}, //end contribs block: function (revObject) { if (mw.config.get("wgUserGroups").indexOf("sysop") === -1) { return null; }					return { text: "Block", href: mw.util.getUrl("Special:Block/" + revObject.user) };				} //end block }, //end userlinks actions: { rollback: function (revObject) { if (revObject.rollbacktoken === undefined) { return null; }					return { text: "Rollback", href: mw.util.getUrl(mw.config.get("wgPageName"), {action: "rollback", from: revObject.user, token: revObject.rollbacktoken}) };				}, //end rollback undo: function (revObject) { return { text: "Undo", href: mw.util.getUrl(mw.config.get("wgPageName"), {action: "edit", undo: revObject.revid, undoafter: revObject.parentid}) };				}, //end undo thank: function (revObject) { return { text: "Thank", href: mw.util.getUrl("Special:Thanks/" + revObject.revid) };				}, //end thank revdel: function (revObject) { if (mw.config.get("wgUserGroups").indexOf("sysop") === -1) { return null; }					return { text: "Delete", href: mw.util.getUrl(mw.config.get("wgPageName"), {action: "revisiondelete", from: revObject.user}) + "&" + mw.util.rawurlencode("ids[" + revObject.revid + "]") };				} //end revdel }, //end actions renderHistoryList: function (limit) { var maxlimit = 500; //default API max; this is a bit of an unhealthy assumption… if (mw.config.get("wgUserGroups").indexOf("sysop") !== -1) { //Same bad assumption, and hackish to boot maxlimit = 5000; }				nothingthree.customRevs.getHistory({					limit: (Math.min(parseInt(limit, 10), maxlimit) || Math.min(parseInt(mw.user.options.get("rclimit"), 10), maxlimit) || 50) //parseInt necessary to force some values to NaN for Math.min				}); //end gethistory & its input object }, //end renderHistoryList testRun: function { if (mw.config.get("wgAction") === "history") { jQuery("#pagehistory").attr("id", "n3-pagehistory").html(""); nothingthree.customRevs.renderHistoryList(50); }			} //end testRun } //end customRevs }; //end nothingthree mw.loader.load("//en.wikipedia.org/w/index.php?title=User:Nihiltres/nothingthree.css&action=raw&ctype=text/css", "text/css"); mw.loader.load("//en.wikipedia.org/w/index.php?title=User:" + mw.util.wikiUrlencode(mw.config.get("wgUserName")) + "/nothingthree-config.js&action=raw&ctype=text/javascript"); //call the activator :) });