Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/********************* this comment is 80 characters long *********************/
(function () {
/* setting strict mode and double-run prevention */
"use strict";
if (window.andrewds1021 && window.andrewds1021.ignore_notifications
&& window.andrewds1021.ignore_notifications.has_run) return;
if (!window.andrewds1021) {
window.andrewds1021 = {
ignore_notifications: {}
};
} else if (!window.andrewds1021.ignore_notifications) {
window.andrewds1021.ignore_notifications = {};
}
window.andrewds1021.ignore_notifications.has_run = true;
/* import dependencies */
importArticles({
type: "script",
articles: [
"u:dev:MediaWiki:GetOnSiteNotifications.js"
]
});
/*
* list of unblockable user groups
* jQuery Deferred for retrieving unblockable users
* list of legacy notification types
* list of UCP notification types
* map from notification card type to notification type
* MediaWiki configuration values
* domain for requests
* whether or not to operate as a whitelist
* whether or not to proceed even if there was an error with notification retrieval
* whether or not to try to retrieve complete lists of users for notifications
* maximum number of notifications to retrieve per request to the service
* maximum number of requests sent to the service
* whether or not to auto-convert settings from AnnouncementsIgnore
* AnnouncementsIgnore settings
* timestamp to generate unique IDs for notification requests
* list of unblockable users, list of filters, running status, dismissal delay,
MediaWiki API object
*/
var unblock_groups = [
"soap",
"staff",
"wiki-specialist"
];
var unblock_def = jQuery.Deferred();
var notif_types = [
"announcement-target",
"discussion-post",
"discussion-upvote",
"post-at-mention",
"thread-at-mention",
"article-comment-at-mention",
"article-comment-reply",
"article-comment-reply-at-mention",
"message-wall-post",
"message-wall-thread",
"talk-page-message"
];
var data_types = {
"announcement": "announcement",
"article-comment-at-mention": "article-comment-at-mention",
"article-comment-reply": "article-comment-reply",
"article-comment-reply-at-mention": "article-comment-reply-at-mention",
"discussion-reply": "replies",
"discussion-upvote-post": "upvote",
"discussion-upvote-reply": "upvote",
"message-wall-reply": "message-wall-reply",
"message-wall-post": "message-wall-post",
"post-at-mention": "post-at-mention",
"talk-page-message": "talk-page",
"thread-at-mention": "thread-at-mention"
};
var config = mw.config.get([
"wgServer",
"skin"
]);
var domain = config.wgServer.split(".").slice(-2).join(".");
var whitelist = !!window.andrewds1021.ignore_notifications.whitelist;
var ignore_errors = !!window.andrewds1021.ignore_notifications.ignore_errors;
var all_users = !!window.andrewds1021.ignore_notifications.all_users;
var limit = window.andrewds1021.ignore_notifications.limit;
if ((typeof limit != "number") || !isFinite(limit) || (limit < 1)
|| (limit > 50)) limit = undefined;
var page_count = window.andrewds1021.ignore_notifications.page_count;
if ((typeof page_count != "number") || isNaN(page_count) || (page_count < 0))
page_count = undefined;
var convert = !window.andrewds1021.ignore_notifications.no_conversion;
var ai_settings = window.announcementsIgnore;
var timestamp = Date.now();
var unblock, filters, running, delay, api;
/* recursive functions to retrieve unblockable users */
/*
* this version is for UCP and requires the following request object
{
action: "query",
list: "allusers",
augroup: unblock_groups.join("|"),
aulimit: 500
}
*/
function getUnblocks(obj) {
api.get(obj.request).then(function (data) {
if (data && data.query && data.query.allusers)
data.query.allusers.forEach(function (val) {
obj.ids.push(String(val.userid));
});
if (data && data["continue"] && data["continue"].aufrom) {
obj.request.aufrom = data["continue"].aufrom;
getUnblocks(obj);
} else {
unblock = obj.ids;
unblock_def.resolve(obj.ids);
}
});
}
/*
* this is an alternate version for UCP and requires the following request object
{
action: "listuserssearchuser",
groups: unblock_groups.join(","),
contributed: 0,
order: "edits",
sort: "asc",
limit: 10,
offset: 0
}
*/
function getUnblocksb(obj) {
unblock = [];
unblock_def.resolve(unblock);
/*
api.get(obj.request).then(function (data) {
if (!data || !data.listuserssearchuser) {
unblock = obj.ids;
unblock_def.resolve(obj.ids);
return;
}
var i;
for (i = 0; (i < obj.request.limit) && data.listuserssearchuser[i]; i++) {
obj.ids.push(data.listuserssearchuser[i].user_id);
}
if (i == obj.request.limit) {
obj.request.offset = obj.request.offset + obj.request.limit;
getUnblocksb(obj);
} else {
unblock = obj.ids;
unblock_def.resolve(obj.ids);
}
});
*/
}
/* after API module is loaded, retrieve unblockable users */
mw.loader.using("mediawiki.api").then(function () {
api = new mw.Api();
getUnblocksb({
ids: [],
request: {
action: "listuserssearchuser",
groups: unblock_groups.join(","),
contributed: 0,
order: "edits",
sort: "asc",
limit: 10,
offset: 0
}
});
});
/*
* remove duplicates from an array, convert a single value to an array, or
return an empty array
* remove values other than non-empty strings and finite numbers
*/
function cleanFilterProperty(prop) {
if (Array.isArray(prop)) {
prop = prop.filter(function (val, idx, arr) {
return (idx == arr.indexOf(val))
&& (((typeof val == "string") && val)
|| ((typeof val == "number") && isFinite(val)));
});
} else if (((typeof prop == "string") && prop)
|| ((typeof prop == "number") && isFinite(prop))) {
prop = [prop];
} else {
prop = [];
}
return prop;
}
/* function to parse an age criteria into milliseconds */
function parseAge(age) {
var ms;
if ((typeof age == "number") && isFinite(age)) {
ms = Math.max(Math.floor(age), 0);
} else if (age && (typeof age == "object")) {
ms = 0;
if ((typeof age.days == "number") && isFinite(age.days))
ms = ms + Math.max(age.days, 0);
ms = 24 * ms;
if ((typeof age.hours == "number") && isFinite(age.hours))
ms = ms + Math.max(age.hours, 0);
ms = 60 * ms;
if ((typeof age.minutes == "number") && isFinite(age.minutes))
ms = ms + Math.max(age.minutes, 0);
ms = 60 * ms;
if ((typeof age.seconds == "number") && isFinite(age.seconds))
ms = ms + Math.max(age.seconds, 0);
ms = Math.floor(1000 * ms);
}
return ms;
}
/*
* recursive function to "clean" an array of filters, convert a single filter to
an array, or return an empty array
* remove values other than non-null objects
* remove duplicate filter references
* prevent filter loops
* "clean" relevant filter properties
*/
function cleanFiltersAndGetNames(filters, ancestors) {
if (!ancestors || !Array.isArray(ancestors)) ancestors = [];
if (Array.isArray(filters)) {
filters = filters.filter(function (val, idx, arr) {
return (typeof val == "object") && val
&& (idx == arr.indexOf(val))
&& (ancestors.indexOf(val) == -1);
});
} else if ((typeof filters == "object") && filters
&& (ancestors.indexOf(filters) == -1)) {
filters = [filters];
} else {
filters = [];
}
filters.forEach(function (val, idx, arr) {
arr[idx] = {
wiki_ids: cleanFilterProperty(val.wiki_ids),
wiki_names: cleanFilterProperty(val.wiki_names),
user_ids: cleanFilterProperty(val.user_ids),
user_names: cleanFilterProperty(val.user_names),
types: cleanFilterProperty(val.types),
age: parseAge(val.age),
not: cleanFiltersAndGetNames(val.not, ancestors.concat(val))
};
});
return filters;
}
/* retrieve filters */
filters = cleanFiltersAndGetNames(window.andrewds1021.ignore_notifications
.filters);
/* if auto-conversion is enabled, convert and append resulting filter */
if (convert && ai_settings && ((ai_settings.option === "opt-in-all")
|| (ai_settings.option === "opt-out-all"))) {
if ((!whitelist && (ai_settings.option == "opt-in-all"))
|| (whitelist && (ai_settings.option != "opt-in-all"))) {
filters.push({
wiki_ids: cleanFilterProperty(ai_settings.exceptWikiIds),
wiki_names: [],
user_ids: [],
user_names: [],
types: ["announcement-target"],
not: []
});
} else {
filters.push({
wiki_ids: [],
wiki_names: [],
user_ids: [],
user_names: [],
types: ["announcement-target"],
not: [{
wiki_ids: cleanFilterProperty(ai_settings.exceptWikiIds),
wiki_names: [],
user_ids: [],
user_names: [],
types: [],
not: []
}]
});
}
}
/*
* recursive function to determine if a notification matches any filters
* wiki matches if no names and no IDs were specified or if name or ID matches
a specified name or ID respectively
* user matches if no names and no IDs were specified or, for a...
* whitelist, any user's
* blacklist, each user's
...name or ID matches a specified name or ID respectively
* type matches if no types were specified or if the type was specified
* age matches if no valid age was specified or, for a...
* whitelist, the notification is at most as old as
* blacklist, the notification is older than
...the specified age
* exception list is considered as the opposite type (whitelist or blacklist)
of its parent
* filter matches if wiki, user, type, and age match but exception list does not
* filter list matches if any filter matches
*/
function matchesFilters(notif, filters, white, time) {
return filters.some(function (filt) {
var wiki = (filt.wiki_ids.length == 0) && (filt.wiki_names.length == 0);
if (!wiki) wiki = filt.wiki_ids.some(function (val) {
return val == notif.community.id;
});
if (!wiki) wiki = filt.wiki_names.some(function (val) {
return val == notif.community.name;
});
var user = (filt.user_ids.length == 0) && (filt.user_names.length == 0);
if (!user) {
if (white) {
user = notif.events.latestActors.some(function (val1) {
return filt.user_ids.some(function (val2) {
return val2 == val1.id;
}) || filt.user_names.some(function (val2) {
return val2 == val1.name;
});
});
} else {
user = notif.events.latestActors.every(function (val1) {
return filt.user_ids.some(function (val2) {
return val2 == val1.id;
}) || filt.user_names.some(function (val2) {
return val2 == val1.name;
});
});
}
}
var type = filt.types.length == 0;
if (!type) type = filt.types.some(function (val) {
return val == notif.events.latestEvent.type;
});
var age = typeof filt.age == "undefined";
if (!age) age = (notif.events.latestEvent.when <
(new Date(time - filt.age)).toISOString()) != white;
return wiki && user && type && age
&& !matchesFilters(notif, filt.not, !white, time);
});
}
/* retrieve dismissal delay and set to 0 if invalid (or 0) */
delay = parseAge(window.andrewds1021.ignore_notifications.delay);
if (!delay) delay = 0;
/*
* function to sort through notifications, identify notifications to dismiss,
attempt notification dismissal, and update display
* restrict sorting to unread notifications
* mark a notification for dismissal if it meets all of the following
* older than the specified delay for dismissing notifications
* lists all users
* does not list any unblockable users
* should be blocked according to filters and filter mode
* unmark a notification for dismissal if it shares a URL with a notification
that is not to be dismissed
* if there are no notifications to dismiss, return
* submit request to mark notifications as read
* if request fails, return
* if request succeeds
* submit request for unread notification count
* if request succeeds, update unread count indicator
* for each notification card marked as unread
* search for matching dismissed notification
* if match found, update notification card
*/
function processNotifications(notifs, info) {
var keep = [];
var dismiss = [];
var req_time = new Date(info.time);
var time = (new Date(req_time - delay)).toISOString();
notifs.filter(function (val) {
return !val.read;
}).forEach(function (val1) {
if (val1.events.latestEvent.when < time
&& (val1.events.totalUniqueActors == val1.events.latestActors.length)
&& val1.events.latestActors.every(function (val2) {
return unblock.indexOf(val2.id) == -1;
})
&& (matchesFilters(val1, filters, whitelist, req_time) != whitelist)) {
dismiss.push(val1);
} else {
keep.push(val1.refersTo.uri);
}
});
dismiss = dismiss.filter(function (val1) {
return !keep.some(function (val2) {
return val2 === val1.refersTo.uri;
});
});
if (!dismiss.length) {
running = false;
return;
}
jQuery.ajax({
url: "https://services." + domain + "/on-site-notifications"
+ "/notifications/mark-as-read/by-uri",
type: "POST",
crossDomain: true,
xhrFields: {
withCredentials: true
},
data: JSON.stringify(dismiss.map(function (val) {
return val.refersTo.uri;
})),
contentType: "application/json; charset=UTF-8"
}).then(function () {
var count_def = jQuery.ajax({
url: "https://services." + domain + "/on-site-notifications"
+ "/notifications/unread-count",
type: "GET",
crossDomain: true,
traditional: true,
xhrFields: {
withCredentials: true
},
dataType: "json",
data: {
startingTimestamp: info.time,
contentType: notif_types
}
});
if (config.skin === "fandomdesktop") {
var notif_section = document.querySelector(".global-navigation__bottom"
+ " .notifications");
if (!notif_section) {
running = false;
return;
}
jQuery.when(count_def, api.get({
action: "notifications",
do: "getNotificationsForUser",
itemsPerPage: 1,
unread: 1
})).then(function (fandom_reply, gamepedia_reply) {
fandom_reply = fandom_reply[0];
gamepedia_reply = gamepedia_reply[0];
if ((typeof fandom_reply.unreadCount != "number")
|| !isFinite(fandom_reply.unreadCount)) return;
var global_counter =
notif_section.querySelector(".notifications__counter");
var fandom_counter = notif_section.querySelector(
"[data-tracking-label=\"notifications-tab-fandom\"]"
+ " .NotificationsDropdown-module_tabTotal__lYSUS");
if (global_counter && (typeof gamepedia_reply.meta.unread == "number")
&& isFinite(gamepedia_reply.meta.unread)) {
var global_unread = fandom_reply.unreadCount
+ gamepedia_reply.meta.unread;
global_counter.textContent = global_unread;
if (global_unread == 0)
global_counter.classList.add("wds-is-hidden");
}
if (fandom_counter) {
fandom_counter.textContent = fandom_reply.unreadCount;
if (fandom_reply.unreadCount == 0)
fandom_counter.classList.add("wds-is-hidden");
}
});
var fandom_list = notif_section.querySelectorAll(".wds-tab__content")[0];
if (!fandom_list) {
running = false;
return;
}
Array.prototype.slice.call(fandom_list.querySelectorAll(
".NotificationCard-module_isUnread__18vlB")).forEach(function (val) {
var num = dismiss.length;
var type = val.getAttribute("data-tracking-label").slice(19);
var link = val.children[0].getAttribute("href");
var search = true;
var notif, i;
for (i = 0; search && (i < num); i++) {
notif = dismiss[i];
if ((link == notif.events.latestEvent.uri)
&& (data_types[type] + "-notification" == notif.type)) {
val.classList.remove(
"NotificationCard-module_isUnread__18vlB");
search = false;
}
}
});
} else {
count_def.then(function (count_reply) {
var counter = document.getElementById("onSiteNotificationsCount");
if (!counter || (typeof count_reply.unreadCount != "number")
|| !isFinite(count_reply.unreadCount)) return;
counter.textContent = count_reply.unreadCount;
if (count_reply.unreadCount == 0)
counter.classList.add("wds-is-hidden");
});
Array.prototype.slice.call(document.querySelectorAll(
"#notificationContainer li.wds-is-unread")).forEach(
function (val) {
var num = dismiss.length;
var type = val.getAttribute("data-type");
var ref = val.getAttribute("data-uri");
var link = val.getElementsByClassName("wds-notification-card"
+ "__outer-body")[0].getAttribute("href");
var search = true;
var notif, i;
for (i = 0; search && (i < num); i++) {
notif = dismiss[i];
if ((link == notif.events.latestEvent.uri)
&& (data_types[type] + "-notification" == notif.type)
&& (ref == notif.refersTo.uri)) {
val.classList.remove("wds-is-unread");
search = false;
}
}
});
}
running = false;
}, function () {
running = false;
});
}
/* functions to request and receive notifications */
function getNotifications() {
if (running) return;
running = true;
timestamp = Date.now();
var info = {
id: "andrewds1021.ignore_notifications." + timestamp,
limit: limit,
page_count: page_count
};
if (all_users) info.mode = "user";
mw.hook("andrewds1021.get_on_site_notifications.run").fire(Object.freeze(info));
}
function recieveNotifications(notifs, info) {
if (info.id !== "andrewds1021.ignore_notifications." + timestamp) return;
timestamp = Date.now();
if (!info.error || ignore_errors) {
processNotifications(notifs, info);
} else {
running = false;
}
}
/* function for initial run */
function firstRun(notifs, info) {
if (((info.counter === 0) && (!all_users || (info.mode !== "notification"))
&& ((info.types.length >= notif_types.length)
&& notif_types.every(function (val1) {
return info.types.indexOf(val1) != -1;
})) && (!limit || !page_count
|| (((info.limit || 50) * info.page_count) >= (limit * page_count))))
|| (info.id === "andrewds1021.ignore_notifications." + timestamp)) {
running = true;
timestamp = Date.now();
mw.hook("andrewds1021.get_on_site_notifications.done").remove(firstRun);
if (!info.error || ignore_errors) {
processNotifications(notifs, info);
} else {
running = false;
}
mw.hook("andrewds1021.get_on_site_notifications.done")
.add(recieveNotifications);
mw.hook("andrewds1021.ignore_notifications.run").add(getNotifications);
mw.hook("andrewds1021.ignore_notifications.ready").fire();
} else {
getNotifications();
}
}
/* after unblock list is loaded, add firstRun to hook */
unblock_def.then(function () {
mw.hook("andrewds1021.get_on_site_notifications.done").add(firstRun);
});
})();