User:Garzfoth/common.js
/* jshint browser: true, devel: true, jquery: true, maxerr: 500, -W082, -W014, scripturl: true, expr: true, undef: true */
/* globals $: false, mw: false, importScript: false, importStylesheet: false, document: false, MutationObserver: false, OO: false, clearTimeout: false, citeTemplate: false */
// http://jshint.com/docs/
// unused: true
// jslint
// long, white, single
// (breaks and/or screws with several parsers in proper form)
// http://stackoverflow.com/questions/14427077/function-declarations-should-not-be-placed-in-blocks-use-a-function-expression
// (less important to fix)
// http://stackoverflow.com/questions/27623822/disable-jshint-wrning-expected-an-assignment-or-function-call-and-instead-saw-a
// (more important to fix)
//
// GENERAL NOTE: The use of commented nowiki tags in a handful of places is done to resolve the issue raised by Brvhelios under "Category:WikiProjects participating in Wikipedia 1.0 assessments" on https://en.wikipedia.org/wiki/User_talk:Garzfoth/common.js. Basically this should be done for any template reference, and at least certain types of special wiki tags.
//
// FROM: https://en.wikipedia.org/wiki/User:Ale_jrb/Scripts
// SINCE: 7/21/2015 (not installed)
// DISABLED: importScript('User:Ale_jrb/Scripts/userhist.js'); //User:Ale_jrb/Scripts
// REASON: Possible conflict, uncertain need, but may want to use in the future.
// Note that the source page may have some other worthwhile ones, but not at the moment.
importScript('User:Technical_13/Scripts/OneClickArchiver.js'); // Backlink: User:Technical_13/Scripts/OneClickArchiver
// Migrated to standalone script on 2021-05-05
importScript('User:Garzfoth/Scripts/CompareLink.js');
// Backlink: User:Garzfoth/Scripts/CompareLink.js
// Migrated to standalone script on 2021-05-05
importScript('User:Garzfoth/Scripts/SafetyEdit.js');
// Backlink: User:Garzfoth/Scripts/SafetyEdit.js
// Migrated to standalone script on 2021-05-05
importScript('User:Garzfoth/Scripts/BetterTwinklePrefs.js');
// Backlink: User:Garzfoth/Scripts/BetterTwinklePrefs.js
// Migrated to standalone script on 2021-05-05
importScript('User:Garzfoth/Scripts/CustomQuickInsertButtons.js');
// Backlink: User:Garzfoth/Scripts/CustomQuickInsertButtons.js
// Migrated to standalone script on 2021-05-05
importScript('User:Garzfoth/Scripts/CustomEditSummaryPresets.js');
// Backlink: User:Garzfoth/Scripts/CustomEditSummaryPresets.js
// Migrated to standalone script on 2021-05-05
importScript('User:Garzfoth/Scripts/WatchlistUpdateNotice.js');
// Backlink: User:Garzfoth/Scripts/WatchlistUpdateNotice.js
//
// Implement conditional logging for debugging purposes
//
// Unless you are currently in the middle of actively debugging an issue, this should be left disabled (set to false).
// If this is left enabled unnecessarily, it will cause issues with performance and console log spam.
var logOverride = true;
// Don't touch this. Declaring this by default is required for the if statement to work correctly (declaring it only when it is true does not work).
// On a side note, that may have simply been a function scope issue...
var enableLogger = false;
if (logOverride === true) {
enableLogger = true;
} else {
var url = window.location.href;
// Extracts "debug" from the URL starting after #, so only if #debug is at the end of the URL (or contained within the URL)
var lastPart = url.substr(url.lastIndexOf('#') + 1);
if (lastPart === "debug") {
enableLogger = true;
}
}
// Based on https://stackoverflow.com/a/25867340
// For the lulz (and since a syntax change is required anyways), I'm changing the name from logifneeded to beaver
// The general syntax change required is logifneeded() => beaver.log()
// I'm not sure if this can be accessible from the global scope if it's placed in a module, especially given the load order dependency. Perhaps declaring it in the common.js file could be enough? Although it may still become inaccessible from modules...
var beaver = (function (enableLogger) {
if (enableLogger === false) {
return {
log: function() {},
warn: function() {},
error: function() {}
}
}
return {
log: function() {
var args = Array.prototype.slice.call(arguments);
console.log.apply(console, args);
},
warn: function() {
var args = Array.prototype.slice.call(arguments);
console.warn.apply(console, args);
},
error: function() {
var args = Array.prototype.slice.call(arguments);
console.error.apply(console, args);
}
}
}(enableLogger));
// test cases
/*
var name = "Alex";
var arr = [1, 2, 3];
var obj = { a:1, b:2, c:3 };
var hello = function(msg){alert(msg);};
console.log("Name: ", name);
beaver.log("Name: ", name);
console.log("Window Debug: ", window);
beaver.log("Window Debug: ", window);
console.error("Something error happen");
beaver.error("Something error happen");
console.warn("Ahh... Warning", arr, obj);
beaver.warn("Ahh... Warning", arr, obj);
console.log("more parameter: ", arr, obj, hello);
beaver.log("more parameter: ", arr, obj, hello);
- /
//
// End of conditional debug logging code
//
//
// Name TBD, as I don't really know what this does anymore. It has something to do with filtering on watchlists. I started off with the oh-so-clear comments "Now for something new...", and "Lets see... hmm... " — those don't really help matters.
// This depends on the conditional debug logging code.
//
// No clue what this snippet is from. I think perhaps this was originally unified, and was later split into the two variants for watchlists and recentchangeslinked?
// || ( mw.config.get('wgCanonicalSpecialPageName') === "RecentChangesLinked" )
if (mw.config.get('wgCanonicalSpecialPageName') === "Watchlist") {
function addWatchlistSpecialUserFilters() {
beaver.log("Started processing a new watchlist...");
// I hope this doesn't screw up stuff like CiteTB...
// FUTURE FEATURE: OFFER A MARK UNREAD BUTTON???
// Declare the watchlist array for future use. Note that sub-arrays cannot be declared until the parent has been declared as an array.
var watchlist = Array();
// Declare iw with initial value 0. This is incremented at the end of each ul.special loop.
// I think this could technically be replaced by parentIndex?
var iw = 0;
// Each section is a ul.special element representing a specific date for commits. It is a container element holding all commits made on that date.
$('ul.special').each(function (parentIndex, parentElement) {
beaver.log("Started working on ul.special:", parentElement);
// Declare a watchlist[iw] sub-array for this value of iw (i.e. for this ul.special loop)
watchlist[iw] = Array();
// Each child is a li element representing an individual commit (entry/line) within the parent section
$(parentElement).children().each(function (childIndex, childElement) {
beaver.log("Started working on ul.special child:", childElement);
// Not entirely sure what this was meant to address at this point, or if it's even working correctly given that there seem to be issues with return [true]...
if ($(childElement).is('div.mw-rcfilters-ui-changesListWrapperWidget-previousChangesIndicator')) {
// https://stackoverflow.com/questions/481601/how-to-skip-to-next-iteration-in-jquery-each-util/481611
return;
}
// The breaking item is a wikidata edit, denoted "D".
// Wikidata edits seem to set the .mw-changeslist-src-mw-wikibase class on the edit's li.mw-changeslist-line element, which should prove helpful...
// Note that the li's contents are totally different for these wikidata edits, they totally lack the span.mw-changeslist-line-inner wrapper child, and just dump a bunch of nasty html into the li directly with only some mixed element class name reuse (but now nested differently!)
// If I'm recalling things correctly, this is actually more or less the way that all watchlist items used to work before the last major watchlist overhaul (the migration to the current "New filters for edit review" version, which has been such a bloody gargantuan headache to adapt scripts to work with)...
// VERY crude TEMPORARY STOPGAP workaround/"fix" for the above
// to be replaced by something that actually fixes the numerous problems posed by this, including not the least the bloody lack of highlighting (and/or ignoring this item if it's the first occurance)
if ($(childElement).is('li.mw-changeslist-line.mw-changeslist-src-mw-wikibase')) {
return false;
// Doesn't work, but kills execution, which is a workaround of sorts I guess.
// Note that this only kills execution for the rest of this specific ul.special. So in the edge case where there's a ul.special without any wikidata elements, then that ul.special will actually process successfully even if there are other ones that failed due to this.
//
// return [true] also didn't work (other issue — it's flat-out broken).
}
// I need to either:
// • (Option A): Somehow convert the parent loops to $.each() (which would require excising jQuery, or at least figuring out how to bridge the JS objects from that to jQuery objects for all calls)
// • (Option B): Just wrap most of this function in a giant negated if statement and call it a day
// e.g. if ( !($(childElement).is('div.mw-rcfilters-ui-changesListWrapperWidget-previousChangesIndicator')) && !($(childElement).is('li.mw-changeslist-line.mw-changeslist-src-mw-wikibase')) ) { [rest of function] }
//
// ultimately I still will eventually need to figure out a real replacement that doesn't depend on negation, but this spagetti code is a nightmare to maintain.
//
// on a side note, the [+] expansion buttons generate (incorrectly) for wikidata items, and throw an error when clicked. I wonder if I could actually fix those to preview wikidata, or if I'll be stuck with conditional hiding of them on wikidata items (and oh boy, trying to identify those in that code will be sooo fun /s).
// .attr() pseudo-documentation
//----------------------------------------
//beaver.log("Processed:", $(childElement).attr('processed'));
// get (may throw undefined):
//$(childElement).attr('processed');
// set:
//$(childElement).attr('processed', ''); // not sure about behavior here?
//$(childElement).attr('processed', 1);
// delete (either one of these work):
//$(childElement).attr('processed', null);
//$(childElement).removeAttr('processed');
//----------------------------------------
//beaver.log("Processed:", $(childElement).attr('processed'));
// Check if we've already processed this element before.
if ($(childElement).attr('processed') != 1) {
beaver.log("This item has not been processed yet, starting processing...");
// Determine the full page key/ID for this page, which contains the page type ID (denoting a type of main, talk, etc) and the page name encoded in CSS class format (this is primarily done by replacing spaces with underscores)
var sliceAndDiceWatchlist = $(childElement).attr('class').match(/watchlist-([0-9]+)-([^\s]*)/);
beaver.log("sliceAndDiceWatchlist:", sliceAndDiceWatchlist);
// Determine the CSS full page ID. Formerly named "cssid".
var cssFullPageID = sliceAndDiceWatchlist[0];
// Determine the CSS page type component. Formerly named "pagetypecss".
var cssPageType = sliceAndDiceWatchlist[1];
// Determine the CSS page name component. Formerly named "pagenamecss".
var cssPageName = sliceAndDiceWatchlist[2];
//
// This block retrieves the commit's author
//
// Other approaches to detecting author. Not sure if these were tested.
//----------------------------------------
//var detectedauthortext = $(childElement).children('a').class('mw-userlink').attr('title').match(/User:[^/]*/);
//var detectedauthortext = $(childElement).children('a').match(/User:[^/]*/);
//
// This was tested and immediately failed for obvious reasons
//var brilliantidea = $(childElement).children('a').$(.mw-userlink).match(/^User:.*$/);
//var detectedauthortext = brilliantidea[0];
//----------------------------------------
// Declare the candidates array for later use
var candidates = Array();
// Specific children are elements with the .mw-userlink class
// Note that the regex User match is actually bad, as it'll fail on IP users (both IPv4 & IPv6)
// For those users, matching based on the URL may work... Alternatively, the regex title attribute match could be adapted further. The current approach is really quite bad.
// Note that IP users have an additional .mw-anonuserlink class.
// Note that there can be multiple potential matches due to the use of user mentions in edit summaries for reversions!
$(childElement).find('a').each(function (matchIndex, matchElement) {
// Set the candidates array entry at the key equal to the match's index to the user retrieved from the (i.e. link) element's title via a regex match. Note that this actually stores an array for the match results rather than the actual raw value of a specific match result.
// TODO: Is there a more optimized way of doing this?
candidates[matchIndex] = $(matchElement).attr('title').match(/^User:[^/]*$/);
// This is what actually throws the error, seemingly due to at least a lack of a .attr('title') on the $(matchElement) it found.
// However since that's just locating by 'a' children under the current $(childElement), it's not going to be very useful...
});
beaver.log("[Author] candidates:", candidates);
// Passing true to $.grep() means that inversion is turned on, and thus we only need to detect null elements
var thematch = $.grep(candidates, function (item, index) { return $.isEmptyObject(item); }, true);
// Because the set method stores an array instead of the raw value, we need to retrieve the raw value for match 0 here
var detectedauthortext = thematch[0];
beaver.log("detectedauthortext:", detectedauthortext);
// End of commit author retrieval block
// Check if we've seen this page before within this specific iteration (i.e. this specific ul.special parent element) by checking for the existence of the relevant array entry, then style it with reduced opacity if we've seen it before.
if (typeof watchlist[iw][cssFullPageID] === 'undefined') {
// If the entry for this iteration and cssFullPageID (which is a unique identifier for this page) in the double-deep watchlist array for this specific iteration (i.e. this specific ul.special parent element) does NOT already exist, then create it by initializing a new array for that set of keys.
watchlist[iw][cssFullPageID] = Array();
} else {
// If the entry DOES already exist, then we've already seen this page at least once before within this specific ul.special element. Therefore set the element style to 65% opacity.
$(childElement).css("opacity", "0.65");
}
// Some sort of prototype for extending the existing system with a way to determine marking, page count, etc through extension of watchlist[iw][cssFullPageID].
//----------------------------------------
// so n is 0 for page, 1 for talk, 3 for user, etc
// but ignore that!!
// DOES watchlist[i][cssFullPageID] EXIST?
// IF NO:
// watchlist[i][cssFullPageID] = Array();
//watchlist[i][cssFullPageID][n] = n
//watchlist[i][cssFullPageID][name] = name
//watchlist[i][cssFullPageID][count] = 1
//watchlist[i][cssFullPageID][marked] = 0
// ==> DISTINGUISH AS PRIMARY <==
// (AKA DO NOTHING?)
// IF YES:
//watchlist[i][cssFullPageID][count] = self + 1
// ==> DISTINGUISH AS SECONDARY <==
// (AKA GREY IT OUT A WEE BIT)
// $(this).css(‘opacity’, 0.65);
//----------------------------------------
// All items (regardless of page type) for the page "WikiProject Medicine" are styled with a unique background color at 15% opacity.
if ((typeof cssPageName !== 'undefined') && (cssPageName == 'WikiProject_Medicine')) {
$(childElement).css("background-color", "rgba(51, 111, 153, 0.15)"); // #3DB870 = rgb(61, 184, 112)
}
// This is just too fucking ugly for me to keep using it
// Style all (talk?) pages with a unique background color at 35% opacity.
//if ((typeof cssPageType !== 'undefined') && (cssPageType == '1')) {
// $(childElement).css("background-color", "rgba(22, 44, 65, 0.35)"); // #3DB870 = rgb(61, 184, 112)
//}
// This has to go last [update: why? was it to allow prior entries that potentially overrode or conflicted with it?]
// Style all of my edits with a unique background color at 35% opacity.
if ((typeof detectedauthortext !== 'undefined') && (detectedauthortext == 'User:Garzfoth')) {
$(childElement).css("background-color", "rgba(61, 184, 112, 0.35)"); // #3DB870 = rgb(61, 184, 112)
}
$(childElement).attr('processed', 1);
} else {
beaver.log("This item was already processed!");
}
//beaver.log("Processed:", $(childElement).attr('processed'));
beaver.log("Processing for this item is now complete.");
});
iw++;
});
beaver.log("Processing of watchlist is complete, here is your watchlist array:", watchlist);
}
/*
function theOnesWhoStartTheFires() {
console.log("A fire has been started! [wikipage.content was fired]");
}
mw.hook('wikipage.content').add(theOnesWhoStartTheFires);
*/
// Okay, so wikipage.content fires once on initial page load, then ***any changes to any options that refresh the watchlist*** will fire it ***twice*** with a tiny but sometimes human perceivable delay in between (which is the origin of certain issues)
/*
function theOnesWhoFightTheFires() {
console.log("The firefighters have arrived!");
}
mw.hook('wikipage.content').add(theOnesWhoFightTheFires);
*/
// Firing is apparently sequential and not affected by hooks.
// AHHHH I think the stupid fucking blur is part of the problem! Damn it. I wonder how to minimize the performance impact of double-processing everything? Not to mention wiping the watchlist array... hmm.
// Execute function when wikipage.content fires.
mw.hook('wikipage.content').add(addWatchlistSpecialUserFilters);
// Execute function directly. Obsolete.
//$(addWatchlistSpecialUserFilters);
}
//
// THIS IS ALSO DEPENDENT ON THE CONDITIONAL LOGGING CODE IMPLEMENTED PRIOR TO THE PREVIOUS FUNCTION
//
// || ( mw.config.get('wgCanonicalSpecialPageName') === "RecentChangesLinked" )
// In theory this should work because of scope, right? Just have to adapt it to
// handle how it's done here... so go to inner for checking name? oh, wait...
// here's how to fix for the in-line ajax stuff: https://www.google.com/search?q=event+fired+when+content+changed+via+ajax&oq=event+fired+when+content+changed+via+ajax&aqs=chrome..69i57.4675j0j7&sourceid=chrome&ie=UTF-8
// (note: make sure that it doesn't trigger onclick, or when loading watchlist, or when opening inline diff)
if ((mw.config.get('wgCanonicalSpecialPageName') === "Recentchanges") || (mw.config.get('wgCanonicalSpecialPageName') === "Recentchangeslinked")) {
function addChangelistSpecialUserFilters() {
beaver.log("Started processing a new set of changes...");
// I hope this doesn't screw up stuff like CiteTB...
// FUTURE FEATURE: OFFER A MARK UNREAD BUTTON???
var changelist = Array();
var i = 0;
// TODO: examine source and add comments as with the watchlist variant
$('ul.special').each(function (parentIndex, parentElement) {
changelist[i] = Array();
// TODO: examine source and add comments as with the watchlist variant
$(this).children().each(function (childIndex, childElement) {
beaver.log(this);
var cssid = $(this).find('a').first().attr('title');
//var cssidinit = $(this).attr('class').match(/watchlist-[0-9]-[^\s]*/);
//var cssid = cssidinit[0];
//beaver.log(cssidinit);
beaver.log(cssid);
if (typeof changelist[i][cssid] === 'undefined') {
changelist[i][cssid] = Array();
} else {
$(this).css("opacity", "0.65");
}
//ignore the below!!
// ----
// so n is 0 for page, 1 for talk, 3 for user, etc
// but ignore that!!
// DOES watchlist[i][CSSID] EXIST?
// IF NO:
// watchlist[i][CSSID] = Array();
//watchlist[i][CSSID][n] = n
//watchlist[i][CSSID][name] = name
//watchlist[i][CSSID][count] = 1
//watchlist[i][CSSID][marked] = 0
// ==> DISTINGUISH AS PRIMARY <==
// (AKA DO NOTHING?)
// IF YES:
//watchlist[i][CSSID][count] = self + 1
// ==> DISTINGUISH AS SECONDARY <==
// (AKA GREY IT OUT A WEE BIT)
// $(this).css(‘opacity’, 0.65);
});
i++;
});
}
mw.hook('wikipage.content').add(addChangelistSpecialUserFilters);
}
// END
// See mw:Reference Tooltips // https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js
//window.pg || $(document).ready(function ($) {
window.pg || $(function () {
// Make sure we are in defined namespaces (notably increased vs. default set)
if ($.inArray(mw.config.get('wgCanonicalNamespace'), ['' , 'Project' , 'Help', 'Draft', 'User', 'Talk', 'Project talk', 'Template' ]) !== -1) {
mw.messages.set({
"RT-enable": "Enable Reference Tooltips",
"RT-disable": "Disable Reference Tooltips",
"RT-disablenote": "Once disabled, Reference Tooltips can be re-enabled using a link in the footer of the page.",
"RT-delay": "Delay before the tooltip appears (in milliseconds): ",
"RT-activationmethod": "Tooltip is activated by:",
"RT-hovering": "hovering",
"RT-clicking": "clicking",
"RT-options": "Reference Tooltips options",
"RT-options-save": "Save settings",
"RT-settings": "Tooltip settings"
});
function toggleRT(o) {
mw.loader.using("mediawiki.cookie", function () {
mw.cookie.set("RTsettings",o+"|"+ settings[1] + "|" + settings[2], {path:"/",expires:90});
location.reload();
});
}
var settings = document.cookie.split("RTsettings=")[1];
settings = settings ? settings.split(";")[0].split("%7C") : [1, 200, +("ontouchstart" in document.documentElement)];
if (settings[0] === 0) {
var footer = $("#footer-places, #f-list");
if (footer.length === 0) {
footer = $("#footer li").parent();
}
footer.append($("
$("")
.text( mw.message( "RT-enable" ) )
//.attr("href","javascript:(function (){})()")
.attr("href","")
//.click(function (){toggleRT(1);})
.click(function (e){
e.preventDefault();
toggleRT(1);
})
));
return;
}
var isTouchscreen = +settings[2],
timerLength = isTouchscreen ? 0 : +settings[1],
settingsMenu;
$(".reference").each(function () {
var tooltipNode, hideTimer, showTimer, checkFlip = false;
function findRef(h) {
h = h.firstChild; h = h && h.getAttribute && h.getAttribute("href"); h = h && h.split("#"); h = h && h[1];
h = h && document.getElementById( h );
h = h && h.nodeName == "LI" && h;
return h;
}
function hide(refLink) {
if (tooltipNode && tooltipNode.parentNode == document.body) {
hideTimer = setTimeout(function () {
$(tooltipNode).animate({opacity: 0}, 100, function () {
document.body.removeChild(tooltipNode);
});
}, isTouchscreen ? 16 : 100);
} else {
$(findRef(refLink)).removeClass("RTTarget");
}
}
function show() {
if (!tooltipNode.parentNode || tooltipNode.parentNode.nodeType === 11) {
document.body.appendChild(tooltipNode);
checkFlip = true;
}
$(tooltipNode).stop().animate({opacity: 1}, 100);
clearTimeout(hideTimer);
}
function openSettingsMenu() {
if (settingsMenu) {
settingsMenu.dialog("open");
} else {
settingsMenu = $("