User:DannyS712/Global watchlist.js
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.
// <nowiki>
/**
* Global Watchlist script
*
* Client-side user script to create a functional "global" watchlist
*
* @author DannyS712
* @version 9.1.0
*/
/* jshint maxerr: 999, esversion: 6 */
/* eslint array-bracket-spacing: ["error", "always"]
array-element-newline: ["error", "consistent"]
comma-dangle: ["error", {"arrays": "always-multiline", "objects": "always-multiline"}]
dot-location: ["error", "property"]
function-call-argument-newline: ["error", "consistent"]
max-lines-per-function: ["error", 65],
multiline-ternary: ["error", "always-multiline"]
object-property-newline: ["error", { "allowAllPropertiesOnSameLine": true }]
quotes: ["error", "single"] */
/* globals GlobalWatchlist, $, mw, OO, Set, Promise */
$(() => { // eslint-disable-line max-lines-per-function
const GlobalWatchlist = {};
window.GlobalWatchlist = GlobalWatchlist;
GlobalWatchlist.cfg = {
debugMode: 100,
name: '[[:m:User:DannyS712/Global watchlist|Global Watchlist]]',
version: '9.1.0',
versionCSS: '6.3',
versionDoc: '1.1',
versionI18n: '1.1',
versionTests: '2.3.1',
};
GlobalWatchlist.init = function (mode) {
GlobalWatchlist.debug('Starting...', mode);
mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:DannyS712/Global watchlist.css&action=raw&ctype=text/css', 'text/css');
const defaultCfg = {
configMode: 0,
confirmAllSites: true,
fastMode: false,
flags: {anon: 0, bot: 0, minor: 0, totalStr: 'unread'},
groupPage: true,
siteList: [ 'en.wikipedia', 'meta.wikimedia', 'commons.wikimedia', 'www.wikidata' ],
testNoActions: false,
types: {edits: true, logEntries: true, newPages: true, totalStr: 'edit|log|new'},
wlStr: 'ids|title|flags|loginfo|parsedcomment|user|tags',
},
metaCfg = {
liveCounter: 0,
mode: -1,
scriptImports: {
/* eslint-disable quotes */
prod: "mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:DannyS712/Global watchlist.js&action=raw&ctype=text/javascript');",
prod2: "mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:DannyS712/Global_watchlist.js&action=raw&ctype=text/javascript');",
prodStable: "mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:DannyS712/Global watchlist/stable.js&action=raw&ctype=text/javascript');",
test: "mw.loader.load('//en.wikipedia.org/w/index.php?title=User:DannyS712 test/Global watchlist.js&action=raw&ctype=text/javascript');",
testStable: "mw.loader.load('//en.wikipedia.org/w/index.php?title=User:DannyS712 test/Global watchlist/stable.js&action=raw&ctype=text/javascript');",
/* eslint-enable quotes */
usingDev: true,
},
},
mwCfg = mw.config.get([ 'skin', 'wgUserName', 'wgUserLanguage' ]),
userSettings = GlobalWatchlist.help.getUserSettings(mwCfg);
GlobalWatchlist.cfg = $.extend({}, GlobalWatchlist.cfg, metaCfg, defaultCfg, userSettings);
new mw.Api().getMessages(
Object.values(GlobalWatchlist.i18n.mwMsgs),
{amenableparser: true, amtitle: 'Special:Watchlist'}
).then((mwUserLocalMsgs) => {
const localMsgs = {},
translations = $.extend({}, GlobalWatchlist.i18n.en, GlobalWatchlist.i18n[mwCfg.wgUserLanguage] || {});
Object.keys(GlobalWatchlist.i18n.mwMsgs).forEach((k) => {
localMsgs[`gw-msg-${k}`] = mwUserLocalMsgs[GlobalWatchlist.i18n.mwMsgs[k]];
});
if (mwCfg.wgUserLanguage !== 'qqx') {
Object.keys(translations).forEach((k) => {
localMsgs[`gw-msg-${k}`] = translations[k];
});
}
mw.messages.set(localMsgs);
GlobalWatchlist.start(mode);
});
};
GlobalWatchlist.mode = function (num) {
GlobalWatchlist.debug('mode', num);
GlobalWatchlist.cfg.mode = num;
switch (num) {
// Loading global watchlist
case 10:
GlobalWatchlist.elements.live.setDisabled(true);
$('#globalWatch-watchlistsLoading').show();
$('#globalWatch-markSeen-all').hide();
$('#globalWatch-watchlistsFeed').hide();
$('#globalWatch-asOf')[0].innerText = '';
break;
// Showing global watchlist
case 11:
GlobalWatchlist.elements.live.setDisabled(GlobalWatchlist.cfg.fastMode);
GlobalWatchlist.elements.refresh.setDisabled(false);
GlobalWatchlist.elements.groupPage.setDisabled(GlobalWatchlist.cfg.fastMode);
GlobalWatchlist.elements.live.setIcon('play');
$('#globalWatch-watchlistsLoading').hide();
$('#globalWatch-watchlistsFeed').show();
GlobalWatchlist.watchlists.checkChangesShown(false);
break;
// Marking all sites as seen, primarily used for status
case 12:
$('span.globalWatch-feed-markSeen > button > span.oo-ui-labelElement-label').each(function () {
this.click();
});
break;
// Live updates running
case 13:
GlobalWatchlist.elements.refresh.setDisabled(true);
GlobalWatchlist.elements.groupPage.setDisabled(true);
GlobalWatchlist.elements.live.setIcon('pause');
GlobalWatchlist.watchlists.runLive();
break;
// Anything else (not supported)
default:
GlobalWatchlist.error('Unsupported mode', num);
}
};
GlobalWatchlist.start = function (mode) {
GlobalWatchlist.cfg.mode = mode;
switch (mode) {
// Start global watchlist
case 1:
GlobalWatchlist.help.setUp(1, 'globalWatchlist');
$('#mw-content-text').empty().append(GlobalWatchlist.watchlists.create());
GlobalWatchlist.watchlists.feed();
GlobalWatchlist.informExtensionLaunched();
break;
// Start global watchlist config
case 2:
GlobalWatchlist.help.setUp(2, 'globalWatchlistSettings');
$('#mw-content-text').empty().append(GlobalWatchlist.settings.create());
GlobalWatchlist.settings.prefill();
GlobalWatchlist.informExtensionLaunched();
break;
// Viewing normal watchlist, add a link
case 3: {
const target = '/wiki/Special:BlankPage/GlobalWatchlist',
text = mw.msg('gw-msg-globalWatchlistLink');
switch (GlobalWatchlist.cfg.skin) {
case 'cologneblue':
case 'minerva':
$('.mw-watchlist-toollinks')[0].childNodes[7].after($('.mw-watchlist-toollinks')[0].childNodes[6].textContent);
$('.mw-watchlist-toollinks')[0].childNodes[8].after(
$('<a>')
.text(text)
.attr('href', target)
.attr('title', text)[0]
);
break;
case 'modern':
case 'monobook':
mw.util.addPortletLink('p-personal', target, text, '', text);
break;
default:
// Vector, timeless
mw.util.addPortletLink('p-views', target, text, '', text);
break;
}
break;
}
// Anything else (tests, qqq documentation, i18n)
default:
GlobalWatchlist.cfg.testNoActions = true;
mw.hook('GlobalWatchlistInternal').fire();
break;
}
};
GlobalWatchlist.informExtensionLaunched = function () {
// Notification that the extension is now available on meta
// No translation support because this will be the last thing added to the script...
OO.ui.alert(
$( '<span>')
.append(
'The ',
$( '<a>' )
.attr( 'href', 'https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:GlobalWatchlist' )
.attr( 'target', '_blank' )
.text( 'GlobalWatchlist extension' ),
' is now available on Meta. Please use that instead. Visit ',
$( '<a>' )
.attr( 'href', 'https://meta.wikimedia.org/wiki/Special:GlobalWatchlistSettings' )
.attr( 'target', '_blank' )
.text( 'Special:GlobalWatchlistSettings' ),
' on Meta to get started.'
),
{ size: 'large' }
);
};
GlobalWatchlist.debugLog = [];
GlobalWatchlist.debug = function (key, msg, level = 1) {
if (GlobalWatchlist.cfg.debugMode >= level) {
console.log(`GlobalWatchlist@${key}:`); // eslint-disable-line no-console
console.log(msg); // eslint-disable-line no-console
}
const logMessage = `${GlobalWatchlist.debugLog.length}: ${key}\t${JSON.stringify(msg)}`;
GlobalWatchlist.debugLog.push(logMessage);
};
GlobalWatchlist.error = function (info, error) {
GlobalWatchlist.debug(`ERROR: ${info}`, error, 0);
alert('GlobalWatchlist error, please check the console!'); // eslint-disable-line no-alert
throw new Error(`Error: ${info} - ${error}`);
};
GlobalWatchlist.Site = function (urlFragment) { // eslint-disable-line max-lines-per-function
this.site = urlFragment.replace(/_/g, '.');
this.siteID = urlFragment.replace(/\./g, '_');
this.divID = `globalWatch-feed-site-${this.siteID}`;
this.$feedDiv = '';
this.isEmpty = false;
this.tags = {};
this.api = function (func, content, name) {
const skipWhenTesting = [ 'updateWatched', 'actuallyMarkSiteAsSeen' ].indexOf(name) > -1;
return new Promise((resolve) => {
this.debug(`API.${name} (called); func & content`, [ func, content ], 1);
if (skipWhenTesting && GlobalWatchlist.cfg.testNoActions) {
this.debug(`API.${name} (skipping, testNoActions); func & content`, [ func, content ], 3);
resolve();
} else {
GlobalWatchlist.help.api(this.site)[func](content).then((response) => {
this.debug(`API.${name} (result); func, content & response`, [ func, content, response ], 2);
resolve(response);
}).catch((error) => {
GlobalWatchlist.error(`API.${this.site}@${name}`, error);
});
}
});
};
this.debug = function (key, msg, level = 1) {
GlobalWatchlist.debug(`${this.site}:${key}`, msg, level);
};
this.actuallyGetWatchlist = function (iter, cnt) {
const that = this;
return new Promise((resolve) => {
const getter = {
action: 'query',
formatversion: 2,
list: 'watchlist',
wllimit: 'max',
wlprop: GlobalWatchlist.cfg.wlStr,
wlshow: GlobalWatchlist.cfg.flags.totalStr,
wltype: GlobalWatchlist.cfg.types.totalStr,
};
if (iter > 1) {
getter.wlcontinue = cnt;
}
if (!GlobalWatchlist.cfg.fastMode) {
getter.wlallrev = true;
}
that.api('get', getter, `actuallyGetWatchlist ${iter}`).then((response) => {
const wlraw = response.query.watchlist;
if (response.continue && response.continue.wlcontinue) {
that.actuallyGetWatchlist(iter + 1, response.continue.wlcontinue).then((innerResponse) => {
resolve(wlraw.concat(innerResponse));
});
} else {
resolve(wlraw);
}
});
});
};
this.changeWatched = function (pageTitle, func) {
this.debug('changeWatched', `Going to ${func}: ${pageTitle}`);
const that = this,
titleReal = pageTitle.replace(/DOUBLEQUOTE/g, '"');
that.api(func, titleReal, 'updateWatched');
that.processUpdateWatched(pageTitle, func === 'unwatch');
if (!GlobalWatchlist.cfg.fastMode) {
that.getAssociatedPageTitle(titleReal).then((associatedTitle) => {
that.processUpdateWatched(associatedTitle, func === 'unwatch');
if (func === 'unwatch') {
GlobalWatchlist.watchlists.checkChangesShown(true);
}
});
}
};
this.getAssociatedPageTitle = function (pageTitle) {
return new Promise((resolve) => {
const getter = {
action: 'parse',
contentmodel: 'wikitext',
formatversion: 2,
onlypst: true,
text: `{{subst:TALKPAGENAME:${pageTitle}}}\n{{subst:SUBJECTPAGENAME:${pageTitle}}}`,
};
this.api('get', getter, 'parseOnlyPST').then((response) => {
const titles = response.parse.text.split('\n');
resolve(titles[1] === pageTitle ? titles[0] : titles[1]);
});
});
};
this.getTagList = function () {
const that = this;
return new Promise((resolve) => {
if (GlobalWatchlist.cfg.fastMode || Object.keys(that.tags).length > 0) {
resolve(true);
} else {
const getter = {
action: 'query',
list: 'tags',
tglimit: 'max',
tgprop: 'displayname',
};
that.api('get', getter, 'getTags').then((response) => {
const asObject = {};
response.query.tags.forEach((t) => {
asObject[t.name] = t.displayname || false ? t.displayname.replace(/<a href="\/wiki\//g, `<a href="https://${that.site}.org/wiki/`) : t.name;
});
that.debug('getTagList', asObject, 3);
that.tags = asObject;
resolve(true);
});
}
});
};
this.getWatchlist = function () {
const that = this;
return new Promise((resolve) => {
that.actuallyGetWatchlist(1, 0).then((wlraw) => {
if (!(wlraw && wlraw[0])) {
that.debug('getWatchlist', 'empty');
that.isEmpty = true;
resolve();
}
that.debug('getWatchlist wlraw', wlraw);
const prelimSummary = GlobalWatchlist.help.rawToSummary(wlraw, this.site);
that.debug('getWatchlist prelimSummary', prelimSummary);
that.makeWikidataList(prelimSummary).then((summary) => {
that.debug('getWatchlist summary', summary);
that.getTagList().then(() => {
const $ul = $('<ul>');
summary.forEach((e) => $ul.append(that.makePageLink(e)));
that.$feedDiv = $('<div>')
.attr('id', that.divID)
.addClass('globalWatch-feed-site')
.append(
$('<h3>')
.append(
$('<a>')
.attr('href', `https://${that.site}.org/wiki/Special:Watchlist`)
.attr('target', '_blank')
.text(that.site),
' (',
$('<a>')
.attr('href', `https://${that.site}.org/wiki/Special:EditWatchlist`)
.attr('target', '_blank')
.text(mw.msg('gw-msg-editWatchlist')),
')'
),
$('<div>')
.addClass('globalWatch-site')
.append(
GlobalWatchlist.OOUI.buttonInput('markSeen', `${that.divID}-seen`, [ 'destructive' ], () => { GlobalWatchlist.help.markSiteSeen(that.site); }, 'check', 'globalWatch-feed-markSeen').$element,
$ul
)
.makeCollapsible()
);
resolve();
});
});
});
});
};
this.makePageLink = function (entry) { // eslint-disable-line max-lines-per-function, complexity
/* eslint-disable one-var */
let $before = false,
$comment = '',
$extraLink = false,
$tags = false,
$user = '';
const pageTitle = encodeURIComponent(entry.title).replace(/'/g, '%27');
const $pageLink = $('<a>')
.attr('href', `https://${this.site}.org/w/index.php?title=${pageTitle}&redirect=no`)
.attr('target', '_blank')
.text(entry.titleMsg || entry.title);
const $historyLink = $('<a>')
.attr('href', `https://${this.site}.org/w/index.php?title=${pageTitle}&action=history`)
.attr('target', '_blank')
.text(mw.msg('gw-msg-history'));
const that = this;
const $unwatchLink = $('<a>')
.addClass('globalWatch-watchunwatch')
.text(mw.msg('gw-msg-unwatch'))
.click(() => {
that.changeWatched(entry.title, 'unwatch');
});
/* eslint-enable one-var */
if (entry.editsbyuser || false) {
$user = entry.editsbyuser.replace('%SITE%', this.site);
} else if (!GlobalWatchlist.cfg.fastMode) {
if (entry.user === false) {
$user = $('<span>')
.addClass('history-deleted')
.text(mw.msg('gw-msg-deleted-user'));
} else if (entry.anon) {
$user = $('<a>')
.attr('href', `https://${this.site}.org/wiki/Special:Contributions/${entry.user}`)
.attr('target', '_blank')
.text(entry.user);
} else {
$user = $('<a>')
.attr('href', `https://${this.site}.org/wiki/User:${entry.user}`)
.attr('target', '_blank')
.text(entry.user);
}
}
if (entry.comment && entry.comment !== '') {
// Need to process links in the parsed comments as raw HTML
// eslint-disable-next-line prefer-template
$comment = $('<span>').html(': ' + entry.comment.replace(/<a href="\/wiki\//g, `<a href="https://${this.site}.org/wiki/`));
}
if (entry.entryType === 'edit') {
$extraLink = $('<a>')
.attr('href', `https://${this.site}.org/w/index.php?diff=${entry.toRev}&oldid=${entry.fromRev}`)
.attr('target', '_blank')
.addClass('globalWatchlist-diff')
.text(entry.editCount === 1 ? mw.msg('gw-msg-diff') : mw.msg('gw-msg-changes', entry.editCount));
} else if (entry.entryType === 'log') {
$extraLink = $('<a>')
.attr('href', `https://${this.site}.org/w/index.php?title=Special:Log&page=${pageTitle}`)
.attr('target', '_blank')
.text(mw.msg('gw-msg-pagelogs'));
}
if (entry.entryType === 'log') {
$before = $('<i>')
.text(`Log: ${entry.logtype}/${entry.logaction}: `);
} else if (entry.minor || entry.bot || entry.entryType === 'new') {
let letters = '';
if (entry.entryType === 'new') {
letters += mw.msg('gw-msg-newPageN');
}
if (entry.minor) {
letters += mw.msg('gw-msg-minorEditM');
}
if (entry.bot) {
letters += mw.msg('gw-msg-botEditB');
}
$before = $('<b>').text(letters);
}
if (entry.tags && entry.tags.length > 0) {
// Need to process links in the parsed description as raw HTML
// eslint-disable-next-line prefer-template
$tags = $('<i>').html('(Tags: ' + entry.tags.map((t) => this.tags[t]).join(', ') + ')');
}
// Actually set up the $row to be returned
// eslint-disable-next-line one-var
const $row = $('<li>');
$row.attr('siteAndPage', `${this.siteID}_${pageTitle}`);
if ($before !== false) {
$row.append($before)
.append(' ');
}
$row.append($pageLink)
.append(' (')
.append($historyLink)
.append(', ');
if ($extraLink !== false) {
$row.append($extraLink)
.append(', ');
}
$row.append($unwatchLink)
.append(') (')
.append($user)
.append($comment)
.append(')');
if ($tags !== false) {
$row.append(' ')
.append($tags);
}
this.debug('makePageLink for entry', [ entry, $row ], 3);
return $row;
};
this.makeWikidataList = function (summary) {
const that = this;
return new Promise((resolve) => {
if (that.site !== 'www.wikidata' || GlobalWatchlist.cfg.fastMode) {
resolve(summary);
} else {
const ids = [],
wdns = [ 0, 120, 146 ];
summary.forEach((e) => {
if (wdns.indexOf(e.ns) > -1) {
e.titleMsg = e.title.replace(/^(?:Property|Lexeme):/, '');
if (ids.indexOf(e.titleMsg) === -1) {
ids.push(e.titleMsg);
}
}
});
that.debug('makeWikidataList - summary, ids', [ summary, ids ]);
if (ids.length === 0) {
resolve(summary);
}
that.getWikidataLabels(ids).then((wdlabels) => {
const lang = GlobalWatchlist.cfg.lang;
summary.forEach((e) => {
if (wdns.indexOf(e.ns) > -1 && wdlabels[e.titleMsg]) {
that.debug('makeWikidataList - have entry', [ e, wdlabels[e.titleMsg] ], 3);
const entryWithLabel = wdlabels[e.titleMsg][e.ns === 146 ? 'lemmas' : 'labels'];
if (entryWithLabel && entryWithLabel[lang] && entryWithLabel[lang].value) {
e.titleMsg += ` (${entryWithLabel[lang].value})`;
}
}
});
resolve(summary);
});
}
});
};
this.getWikidataLabels = function (ids) {
const that = this;
return new Promise((resolve) => {
that.debug('getWikidataLabels ids', ids);
const lang = GlobalWatchlist.cfg.lang,
wdgetter = {
action: 'wbgetentities',
formatversion: 2,
ids: ids.slice(0, 50),
languages: lang,
props: 'labels',
};
that.api('get', wdgetter, 'getWikidataLabels').then((response) => {
const wdlabels = response.entities;
that.debug('getWikidataLabels wdlabels', wdlabels);
if (ids.length > 50) {
that.getWikidataLabels(ids.slice(50)).then((extraLabels) => {
const bothLabels = $.extend({}, wdlabels, extraLabels);
that.debug('getWikidataLabels bothLabels', bothLabels, 3);
resolve(bothLabels);
});
} else {
resolve(wdlabels);
}
});
});
};
this.markAsSeen = function () {
this.debug('markSiteAsSeen', 'marking');
const setter = {
action: 'setnotificationtimestamp',
entirewatchlist: true,
timestamp: GlobalWatchlist.cfg.time.toISOString(),
};
this.api('postWithEditToken', setter, 'actuallyMarkSiteAsSeen');
if (GlobalWatchlist.cfg.mode === 11 || GlobalWatchlist.cfg.mode === 13) {
this.debug('markSiteAsSeen', 'hiding');
$(`#${this.divID} > *:not(h3)`).hide();
}
GlobalWatchlist.watchlists.checkChangesShown(true);
};
this.processUpdateWatched = function (pageTitle, unwatched) {
this.debug('processUpdateWatched', `Proccessing after ${unwatched ? 'unwatching' : 'rewatching'}: ${pageTitle}`);
const encodedTitle = encodeURIComponent(pageTitle).replace(/'/g, '%27').replace(/DOUBLEQUOTE/g, '%22'),
msg = mw.msg(`gw-msg-${unwatched ? 'rewatch' : 'unwatch'}`),
that = this,
$links = $(`li[siteAndPage='${this.siteID}_${encodedTitle}'] > a.globalWatch-watchunwatch`); // eslint-disable-line sort-vars
$(`li[siteAndPage='${this.siteID}_${encodedTitle}']`)[unwatched ? 'addClass' : 'removeClass']('globalWatch-strike');
$links.each(function () {
$(this).off('click');
$(this).on('click', () => {
that.changeWatched(pageTitle, unwatched ? 'watch' : 'unwatch');
});
$(this).text(msg);
});
};
};
GlobalWatchlist.settings = {
actuallySaveOptions: function (newOptions) {
new mw.Api().edit(GlobalWatchlist.cfg.globalJS, (revision) => {
const imports = GlobalWatchlist.cfg.scriptImports;
let content = revision.content.replace(imports.prod2, imports.prod);
if (GlobalWatchlist.cfg.configMode === 2) {
content = content.replace(/window\.GlobalWatchlistConfig\s*=\s*{[^}]*\};\n/, newOptions);
} else {
content = content.replace(imports.prod, newOptions + imports.prod);
}
return {
summary: `Updating global watchlist settings (${GlobalWatchlist.cfg.name} v. ${GlobalWatchlist.cfg.version})`,
text: content,
};
}).done(() => {
GlobalWatchlist.debug('Settings.actuallySaveOptions', 'should be done');
GlobalWatchlist.help.notify('settingsSaved');
GlobalWatchlist.help.notify('redirecting');
if (GlobalWatchlist.cfg.debugMode < 2) {
setTimeout(() => {
window.location.href = window.location.href.replace('GlobalWatchlistConfig', 'GlobalWatchlist');
}, 2000);
}
}).fail(() => {
GlobalWatchlist.debug('Settings.actuallySaveOptions', 'something went wrong');
GlobalWatchlist.help.notify('savingFailed');
});
},
addRow: function (start = '') {
const num = GlobalWatchlist.elements.sites.length,
row = new OO.ui.ActionFieldLayout(
new OO.ui.TextInputWidget({
classes: [ 'globalWatch-site-text' ],
value: start,
}),
GlobalWatchlist.OOUI.buttonInput('remove', '', [ 'destructive' ], () => { GlobalWatchlist.elements.sites[num].toggle(); })
);
GlobalWatchlist.elements.sitelist.addItems([ row ]);
GlobalWatchlist.elements.sites.push(row);
return row;
},
create: function () { return [
GlobalWatchlist.OOUI.buttonInputSimpleE('resetChanges', GlobalWatchlist.settings.prefill, 'history'),
GlobalWatchlist.OOUI.buttonE('Back', 'GlobalWatchlist', 'previous'),
GlobalWatchlist.OOUI.buttonInput(GlobalWatchlist.cfg.scriptImports.usingDev ? 'switch-stable' : 'switch-dev', 'globalWatch-switch-import', [ 'destructive' ], GlobalWatchlist.settings.switchBranch).$element,
$('<hr>'),
$('<br>'),
$('<div>')
.attr('id', 'globalWatch-sites')
.append(
GlobalWatchlist.OOUI.labelE('siteList'),
$('<div>')
.attr('id', 'globalWatch-sites-list'),
GlobalWatchlist.elements.sitelist.$element,
GlobalWatchlist.OOUI.buttonInputSimpleE('add', GlobalWatchlist.settings.addRow, 'add'),
GlobalWatchlist.OOUI.buttonInputSimpleE('save', GlobalWatchlist.settings.saveChanges, 'bookmark')
),
$('<div>')
.attr('id', 'globalWatch-filters')
.append(
$('<div>')
.attr('id', 'globalWatch-filters-label')
.append(GlobalWatchlist.OOUI.labelE('filters')),
$('<hr>'),
$('<div>')
.attr('id', 'globalWatch-filters-list')
.append(
GlobalWatchlist.elements.anon.$element,
GlobalWatchlist.elements.bot.$element,
GlobalWatchlist.elements.minor.$element
),
$('<hr>'),
$('<div>')
.attr('id', 'globalWatch-settings-other')
.append(
GlobalWatchlist.OOUI.optionsE('changetypes', [ 'edits', 'logEntries', 'newPages' ]),
GlobalWatchlist.OOUI.optionsE('otherOptions', [ 'groupPage', 'confirmAllSites', 'fastMode' ])
)
),
]; },
prefill: function () {
const c = $.extend({}, GlobalWatchlist.cfg, GlobalWatchlist.cfg.types, GlobalWatchlist.cfg.flags);
[ 'anon', 'bot', 'minor' ].forEach((s) => {
GlobalWatchlist.elements[s].selectItemByData(c[s]);
});
[ 'edits', 'logEntries', 'newPages', 'groupPage', 'confirmAllSites', 'fastMode' ].forEach((s) => {
GlobalWatchlist.elements[s].setSelected(c[s]);
});
GlobalWatchlist.elements.sitelist.clearItems();
GlobalWatchlist.cfg.siteList.forEach((s, i) => {
GlobalWatchlist.elements.sites[i].toggle(true);
GlobalWatchlist.elements.sitelist.addItems([ GlobalWatchlist.elements.sites[i] ]);
$('.globalWatch-site-text:last > input')[0].value = s;
});
GlobalWatchlist.elements.sites[GlobalWatchlist.cfg.siteList.length].toggle(true);
GlobalWatchlist.elements.sitelist.addItems([ GlobalWatchlist.elements.sites[GlobalWatchlist.cfg.siteList.length] ]);
$('.globalWatch-site-text:last > input')[0].value = '';
},
saveChanges: function () {
const e = GlobalWatchlist.elements,
settings = {
anonFilter: e.anon.findSelectedItem().data,
botFilter: e.bot.findSelectedItem().data,
confirmAllSites: e.confirmAllSites.isSelected(),
fastMode: e.fastMode.isSelected(),
groupPage: e.groupPage.isSelected(),
minorFilter: e.minor.findSelectedItem().data,
showEdits: e.edits.isSelected(),
showLogEntries: e.logEntries.isSelected(),
showNewPages: e.newPages.isSelected(),
sites: [ ...new Set($('div.oo-ui-actionFieldLayout:visible .globalWatch-site-text > input').filter(function () { return this.value && this.value !== ''; }).map((s, f) => f.value).toArray()) ],
};
GlobalWatchlist.settings.validate(settings).then((validation) => {
GlobalWatchlist.debug('Settings.saveChanges - chosen and settingsValidation', [ settings, validation ]);
if (validation.length > 0) {
OO.ui.alert(new OO.ui.HtmlSnippet(`<ul><li>${validation.join('</li><li>')}</li></ul>`), {title: 'Invalid settings'});
} else if (GlobalWatchlist.cfg.testNoActions) {
GlobalWatchlist.debug('Settings.saveChanges - skipping, testNoActions', settings);
} else {
// eslint-disable-next-line prefer-template, prefer-named-capture-group
GlobalWatchlist.settings.actuallySaveOptions('window.GlobalWatchlistConfig = ' + JSON.stringify(settings, null, ' ').replace(/\n/g, '').replace(/(\S)}/, '$1 }') + ';\n');
}
});
},
switchBranch: function () {
new mw.Api().edit(GlobalWatchlist.cfg.globalJS, (revision) => {
const allimports = GlobalWatchlist.cfg.scriptImports,
currentImport = allimports[allimports.usingDev ? 'prod' : 'prodStable'],
nextImport = allimports[allimports.usingDev ? 'prodStable' : 'prod'];
return {
summary: `Switching global watchlist branch (switched from ${GlobalWatchlist.cfg.name} v. ${GlobalWatchlist.cfg.version})`,
text: revision.content.replace(currentImport, nextImport),
};
}).then(() => {
GlobalWatchlist.debug('Settings.switchBranch', 'should be done');
if (GlobalWatchlist.cfg.debugMode < 2) {
setTimeout(() => {
window.location.reload();
}, 1000);
}
});
},
validate: function (settings) {
return new Promise((resolve) => {
new mw.Api().get({
action: 'query',
meta: 'globaluserinfo',
guiprop: 'merged', // eslint-disable-line sort-keys
}).done((response) => {
const accounts = response.query.globaluserinfo.merged.map((s) => s.url.replace(/https:\/\/|\.org/g, '')),
badSites = settings.sites.filter((s) => accounts.indexOf(s) === -1);
let errors = [];
GlobalWatchlist.debug('Settings.validate - bad sites', badSites);
if (settings.sites.length === 0) {
errors.push('nosites');
}
if (!settings.showEdits && !settings.showNewPages && !settings.showLogEntries) {
errors.push('nochanges');
}
if (settings.anonFilter === 1 && settings.botFilter === 1) {
errors.push('anonbot');
}
if (settings.anonFilter === 1 && settings.minorFilter === 1) {
errors.push('anonminor');
}
errors = errors.map((e) => mw.msg(`gw-msg-settings-error-${e}`));
if (badSites.length > 0) {
errors.push(mw.msg('gw-msg-settings-error-badsites', badSites.join(', ')));
}
resolve(errors);
});
});
},
};
GlobalWatchlist.watchlists = {
checkChangesShown: function (shouldReload) {
const sitesWithChangesShown = $('#globalWatch-feedCollector > div.globalWatch-feed-site > div.globalWatch-site ul:has(li:visible:not(.globalWatch-strike))').length;
if (sitesWithChangesShown > 1 && GlobalWatchlist.cfg.mode !== 12) {
$('#globalWatch-markSeen-all').show();
} else if (sitesWithChangesShown === 1) {
$('#globalWatch-markSeen-all').hide();
} else if (shouldReload) {
setTimeout(GlobalWatchlist.watchlists.feed, 1000);
}
},
create: function () { return [
$('<div>')
.attr('id', 'globalWatch-toolbar')
.append(
GlobalWatchlist.elements.live.$element,
GlobalWatchlist.elements.groupPage.$element,
GlobalWatchlist.elements.refresh.$element,
GlobalWatchlist.OOUI.buttonE('Settings', 'GlobalWatchlistConfig', 'settings'),
GlobalWatchlist.OOUI.buttonInput('markSeen-all', 'globalWatch-markSeen-all', [ 'primary', 'destructive' ], GlobalWatchlist.watchlists.markAllAsSeen, 'checkAll').$element
),
$('<div>')
.attr('id', 'globalWatch-asOf'),
new OO.ui.ProgressBarWidget({id: 'globalWatch-watchlistsLoading'}).$element,
$('<div>')
.attr('id', 'globalWatch-watchlistsFeed'),
]; },
feed: function () {
if (GlobalWatchlist.cfg.mode === 13) {
return;
}
GlobalWatchlist.mode(10);
GlobalWatchlist.watchlists.refresh().then(() => GlobalWatchlist.mode(11));
},
markAllAsSeen: function () {
if (GlobalWatchlist.cfg.confirmAllSites === false) {
GlobalWatchlist.debug('watchlists.markSeen-all', 'Marking all sites as seen without confirmation');
GlobalWatchlist.mode(12);
} else {
OO.ui.confirm(mw.msg('gw-msg-markSeen-allConfirm')).done((confirmed) => {
GlobalWatchlist.debug('watchlists.markAllAsSeen', confirmed ? 'Marking all sites as seen' : 'Aborting...');
if (confirmed) {
GlobalWatchlist.mode(12);
}
});
}
},
refresh: function () {
GlobalWatchlist.debug('watchlists.refresh', 'starting refresh');
GlobalWatchlist.cfg.time = new Date();
return new Promise((resolve) => {
Promise.all(GlobalWatchlist.sites.map((s) => s.getWatchlist())).then(() => {
const $div = $('<div>').attr('id', 'globalWatch-feedCollector'),
emptySites = [];
let showChangesLabel = false;
GlobalWatchlist.sites.forEach((s) => {
GlobalWatchlist.debug('watchlists.refrsh site loop, a site', s, 3);
if (s.isEmpty) {
emptySites.push(s.site);
} else {
showChangesLabel = true;
$div.append(s.$feedDiv);
}
});
if (showChangesLabel) {
$div
.prepend(GlobalWatchlist.OOUI.labelE('changesFeed'))
.append($('<hr>'));
}
if (emptySites[0]) {
const $ul = $('<ul>');
[ ...new Set(emptySites) ].forEach((s) => {
$ul.append(
$('<li>')
.addClass('globalWatch-emptyWatchlist')
.append(
$('<a>')
.attr('href', `https://${s}.org/wiki/Special:Watchlist`)
.text(s),
' (',
$('<a>')
.attr('href', `https://${s}.org/wiki/Special:EditWatchlist`)
.text(mw.msg('gw-msg-editWatchlist')),
')'
)
);
});
$div.append(
GlobalWatchlist.OOUI.labelE('emptyFeed'),
$('<div>')
.addClass('globalWatch-emptySites')
.addClass('mw-collapsed')
.append(
$('<div>')
.addClass('globalWatch-col')
.append($ul)
)
.makeCollapsible()
);
}
$('#globalWatch-watchlistsFeed')
.empty()
.append($div);
GlobalWatchlist.watchlists.runLive();
$('#globalWatch-asOf')[0].innerText = mw.msg('gw-msg-asOf', GlobalWatchlist.cfg.time.toUTCString());
resolve();
}).catch((error) => {
GlobalWatchlist.debug('watchlists.refresh ERROR', error);
});
});
},
runLive: function () {
if (GlobalWatchlist.cfg.mode === 13) {
GlobalWatchlist.debug('watchlists.runLive - counter', GlobalWatchlist.cfg.liveCounter++);
setTimeout(GlobalWatchlist.watchlists.refresh, 7500);
}
},
};
GlobalWatchlist.help = {
api: function (site) {
return new mw.ForeignApi(`//${site}.org/w/api.php`);
},
convertEdits: function (edits, site) {
const finalEdits = [];
Object.values(edits).forEach((e) => {
const pagebase = {
entryType: 'edit',
ns: e.ns,
title: e.title,
};
if (!GlobalWatchlist.cfg.groupPage || e.each.length === 1) {
e.each.forEach((f) => {
finalEdits.push($.extend({}, pagebase, {
anon: f.anon || false,
bot: f.bot,
comment: f.parsedcomment || '',
editCount: 1,
fromRev: f.old_revid,
minor: f.minor,
tags: f.tags,
toRev: f.revid,
user: f.user,
}));
});
} else { /* eslint-disable no-extra-parens */
const distinctUsers = [ ...new Set(e.each.map((f) => f.user)) ],
userEntries = [];
distinctUsers.forEach((u) => {
const userEdits = e.each.filter((f) => (f.user === u)),
userLink = (userEdits[0].user === false) ? `<span class="history-deleted">${mw.msg('gw-msg-deleted-user')}</span>` : `<a href="https://${site}.org/wiki/${userEdits[0].anon || false ? 'Special:Contributions/' : 'User:'}${u}" target="_blank">${u}</a>`;
userEntries.push(`${userLink}${userEdits.length > 1 ? ` ${mw.msg('gw-msg-editCount', userEdits.length)}` : ''}`);
});
finalEdits.push($.extend({}, pagebase, {
bot: e.each.map((f) => f.bot).reduce((a, b) => (a && b)),
editCount: e.each.length,
editsbyuser: userEntries.join(', '),
fromRev: e.each.map((f) => f.old_revid).reduce((a, b) => (a > b ? b : a)),
minor: e.each.map((f) => f.minor).reduce((a, b) => (a && b)),
tags: [],
toRev: e.each.map((f) => f.revid).reduce((a, b) => (a > b ? a : b)),
}));
} /* eslint-enable no-extra-parens */
});
return finalEdits;
},
flag: function (setting, msg) {
switch (setting) {
case 1:
return `|${msg}`;
case 2:
return `|!${msg}`;
default:
return '';
}
},
getUserSettings: function (mwCfg) {
const cfg = {
debugMode: mwCfg.wgUserName === 'DannyS712 test' ? 2 : 1,
globalJS: `User:${mwCfg.wgUserName}/global.js`,
lang: mwCfg.wgUserLanguage,
skin: mwCfg.skin,
};
if (typeof window.GlobalWatchlistConfig !== 'undefined') {
GlobalWatchlist.debug('help.getUserSettings GlobalWatchlistConfig', window.GlobalWatchlistConfig);
const WGWC = window.GlobalWatchlistConfig,
flags = {anon: WGWC.anonFilter || 0, bot: WGWC.botFilter || 0, minor: WGWC.minorFilter || 0},
types = {edits: WGWC.showEdits !== false, logEntries: WGWC.showLogEntries !== false, newPages: WGWC.showNewPages !== false};
flags.totalStr = [ 'unread', GlobalWatchlist.help.flag(flags.bot, 'bot'), GlobalWatchlist.help.flag(flags.anon, 'anon'), GlobalWatchlist.help.flag(flags.minor, 'minor') ].join('');
types.totalStr = ((types.edits ? 'edit|' : '') + (types.newPages ? 'new|' : '') + (types.logEntries ? 'log|' : '')).replace(/\|+$/, '');
if (WGWC.fastMode) {
cfg.fastMode = true;
cfg.wlStr = 'ids|title|flags|loginfo';
}
cfg.configMode = 2;
cfg.siteList = WGWC.sites;
cfg.groupPage = WGWC.groupPage;
cfg.confirmAllSites = WGWC.confirmAllSites;
cfg.flags = flags;
cfg.types = types;
}
return cfg;
},
markSiteSeen: function (site) {
new GlobalWatchlist.Site(site).markAsSeen();
},
notify: function (msg) {
mw.notify(mw.msg(`gw-msg-notify-${msg}`), {title: 'Global Watchlist'});
},
rawToSummary: function (entries, site) {
const edits = {},
logEntries = [],
newPages = [];
entries.forEach((e) => {
if (e.userhidden) {
e.user = false;
}
if (e.type === 'edit') {
if (typeof edits[e.pageid] === 'undefined') {
edits[e.pageid] = {each: [ e ], ns: e.ns, title: e.title};
} else {
edits[e.pageid].each.push(e);
}
} else {
const entryBase = {
anon: e.anon || false,
comment: e.parsedcomment || '',
entryType: e.type,
ns: e.ns,
tags: e.tags,
title: e.title,
user: e.user,
};
if (e.type === 'new') {
newPages.push(entryBase);
} else if (e.type === 'log') {
logEntries.push($.extend(entryBase, {
logaction: e.logaction,
logtype: e.logtype,
}));
}
}
});
return [ ...newPages, ...GlobalWatchlist.help.convertEdits(edits, site), ...logEntries ];
},
setUp: function (mode, page) {
window.document.title = mw.msg(`gw-msg-title-${page}`);
$(`#${GlobalWatchlist.cfg.skin === 'minerva' ? 'section_0' : 'firstHeading'}`)[0].innerText = mw.msg(`gw-msg-heading-${page}`);
if (mode === 1) {
GlobalWatchlist.elements = {
groupPage: GlobalWatchlist.OOUI.toggleButton('groupPage', GlobalWatchlist.cfg.groupPage && !GlobalWatchlist.cfg.fastMode, () => {
GlobalWatchlist.cfg.groupPage = GlobalWatchlist.elements.groupPage.value;
GlobalWatchlist.watchlists.feed();
}),
live: GlobalWatchlist.OOUI.toggleButton('live', false, () => {
GlobalWatchlist.mode(GlobalWatchlist.elements.live.value ? 13 : 11);
}),
refresh: GlobalWatchlist.OOUI.buttonInput('refresh', 'globalWatch-refresh', [ 'primary', 'progressive' ], GlobalWatchlist.watchlists.feed, 'reload'),
};
GlobalWatchlist.sites = GlobalWatchlist.cfg.siteList.map((s) => new GlobalWatchlist.Site(s));
} else if (mode === 2) {
GlobalWatchlist.elements = {
anon: GlobalWatchlist.OOUI.filter('anon'),
bot: GlobalWatchlist.OOUI.filter('bot'),
confirmAllSites: GlobalWatchlist.OOUI.checkBox('option-confirmAllSites'),
edits: GlobalWatchlist.OOUI.checkBox('show-edits'),
fastMode: GlobalWatchlist.OOUI.checkBox('option-fastMode'),
groupPage: GlobalWatchlist.OOUI.checkBox('option-groupPage'),
logEntries: GlobalWatchlist.OOUI.checkBox('show-logEntries'),
minor: GlobalWatchlist.OOUI.filter('minor'),
newPages: GlobalWatchlist.OOUI.checkBox('show-newPages'),
sitelist: new OO.ui.FieldsetLayout(),
sites: [],
};
GlobalWatchlist.cfg.siteList.forEach((s) => GlobalWatchlist.settings.addRow(s));
GlobalWatchlist.settings.addRow();
}
$('#content div.mw-indicators').append(
$('<div>')
.addClass('mw-indicator')
.append(
$('<a>')
.addClass('mw-helplink')
.attr('href', 'https://meta.wikimedia.org/wiki/User:DannyS712/Global_watchlist')
.attr('target', '_blank')
.text(mw.msg('gw-msg-help'))
)
);
},
};
GlobalWatchlist.OOUI = {
buttonE: function (msg, target, icon) {
return new OO.ui.ButtonWidget({
flags: [ 'progressive' ],
href: `/wiki/:m:Special:BlankPage/${target}`,
icon: icon,
label: mw.msg(`gw-msg-globalWatchlist${msg}Link`),
}).$element;
},
buttonInput: function (msg, id, flags, onClick, icon, classes) { // eslint-disable-line max-params
return new OO.ui.ButtonInputWidget({
classes: [ classes ],
flags: flags,
icon: icon,
id: id,
label: mw.msg(`gw-msg-${msg}`),
}).on('click', onClick);
},
buttonInputSimpleE: function (msg, onClick, icon) {
return GlobalWatchlist.OOUI.buttonInput(msg, `globalWatch-${msg}`, [], onClick, icon).$element;
},
checkBox: function (label) {
return new OO.ui.CheckboxMultioptionWidget({label: mw.msg(`gw-msg-${label}`)});
},
filter: function (filter) {
return new OO.ui.RadioSelectWidget({
items: [ 'either', `only-${filter}`, `not-${filter}` ].map((m, i) => new OO.ui.RadioOptionWidget({data: i, label: mw.msg(`gw-msg-filter-${m}`)})),
text: mw.msg(`gw-msg-filter-${filter}`),
});
},
labelE: function (msg) {
return new OO.ui.LabelWidget({label: mw.msg(`gw-msg-${msg}`)}).$element;
},
optionsE: function (msg, items) {
return [
GlobalWatchlist.OOUI.labelE(msg),
new OO.ui.CheckboxMultiselectWidget({items: items.map((i) => GlobalWatchlist.elements[i])}).$element,
];
},
toggleButton: function (msg, value, onClick) {
return new OO.ui.ToggleButtonWidget({
disabled: GlobalWatchlist.cfg.fastMode,
label: mw.msg(`gw-msg-option-${msg}`),
value: value,
}).on('click', onClick);
},
};
GlobalWatchlist.i18n = {
en: {
'asOf': 'As of $1',
'changesFeed': 'Sites with changes',
'emptyFeed': 'Sites with no changes',
'filter-anon': 'Anonymous edits',
'filter-bot': 'Bot edits',
'filter-either': 'Either',
'filter-not-anon': 'Only edits made by logged-in users',
'filter-not-bot': 'Only non-bot edits',
'filter-not-minor': 'Only non-minor edits',
'filter-only-anon': 'Only edits made anonymously',
'filter-only-bot': 'Only bot edits',
'filter-only-minor': 'Only minor edits',
'globalWatchlistBackLink': 'Back to Global watchlist',
'globalWatchlistLink': 'Global watchlist',
'globalWatchlistSettingsLink': 'Settings',
'heading-globalWatchlist': 'Global watchlist',
'heading-globalWatchlistSettings': 'Global watchlist settings',
'markSeen': 'Mark as seen',
'markSeen-all': 'Mark all sites as seen',
'markSeen-allConfirm': 'Are you sure you want to mark sites as seen?',
'notify-redirecting': 'Redirecting back to global watchlist...',
'notify-savingFailed': 'Failed to save settings. Please try again',
'option-confirmAllSites': 'Require confirmation before marking all sites as seen',
'option-fastMode': 'Load faster, at the expense of less detail',
'otherOptions': 'Other settings',
'refresh': 'Refresh',
'resetChanges': 'Reset changes made',
'rewatch': 'rewatch',
'settings-error-anonbot': 'Anonymous users cannot make bot edits',
'settings-error-anonminor': 'Anonymous users cannot make minor edits',
'settings-error-badsites': 'Invalid site(s): $1',
'settings-error-nochanges': 'No change types selected',
'settings-error-nosites': 'No sites selected',
'show-edits': 'Show page edits',
'show-logEntries': 'Show log entries',
'show-newPages': 'Show new page creations',
'siteList': 'User defined site list',
'switch-dev': 'Switch to development version',
'switch-stable': 'Switch to stable version',
'title-globalWatchlist': 'Global watchlist',
'title-globalWatchlistSettings': 'Global watchlist settings',
'unwatch': 'unwatch',
},
mwMsgs: {
'add': 'htmlform-cloner-create',
'botEditB': 'boteditletter',
'changes': 'nchanges',
'changetypes': 'rcfilters-filtergroup-changetype',
'deleted-user': 'rev-deleted-user',
'diff': 'diff',
'editCount': 'ntimes',
'editWatchlist': 'watchlistedit-normal-title',
'filter-minor': 'rcfilters-filter-minor-label',
'filters': 'whatlinkshere-filters',
'help': 'helppage-top-gethelp',
'history': 'history_small',
'minorEditM': 'minoreditletter',
'newPageN': 'newpageletter',
'notify-settingsSaved': 'savedprefs',
'option-groupPage': 'rcfilters-group-results-by-page',
'option-live': 'rcfilters-liveupdates-button',
'pagelogs': 'sp-contributions-logs',
'remove': 'htmlform-cloner-delete',
'save': 'saveprefs',
},
qqq: {
'asOf': 'When the feed was last refreshed; $1 = timestamp',
'changesFeed': 'Label for list of sites with changes to show',
'emptyFeed': 'Label for list of sites with no changes to show',
'filter-anon': 'Label for the anonymous edits filter',
'filter-bot': 'Label for the bot edits filter',
'filter-either': 'Option to not filter based on anonymous/bot/minor edits',
'filter-not-anon': 'Option to only show edits not made by anonymous users',
'filter-not-bot': 'Option to only show edits not made by bots',
'filter-not-minor': 'Option to only show edits not marked as minor',
'filter-only-anon': 'Option to only show edits made by anonymous users',
'filter-only-bot': 'Option to only show edits made by bots',
'filter-only-minor': 'Option to only show edits marked as minor',
'globalWatchlistBackLink': 'Label for the link to the global watchlist from the settings page',
'globalWatchlistLink': 'Label for the link to the global watchlist from the normal watchlist page',
'globalWatchlistSettingsLink': 'Label for the link to the settings page from the global watchlist',
'heading-globalWatchlist': '"Global watchlist" header',
'heading-globalWatchlistSettings': 'Settings page header',
'markSeen': 'Button to mark a specific site as seen',
'markSeen-all': 'Button to mark all sites as seen',
'markSeen-allConfirm': 'Confirmation message for marking all sites as seen',
'notify-redirecting': 'Notification of redirecting to global watchlist after settings saved',
'notify-savingFailed': 'Notification that settings were not saved successfully',
'option-confirmAllSites': 'Label for toggle option to opt-out of confirming before marking all sites as seen',
'option-fastMode': 'Label for toggle option to activate fast mode',
'otherOptions': 'Label for other script configuration options',
'refresh': 'Label for the refresh button',
'resetChanges': 'Label for the settings button to reset changes',
'rewatch': 'Label for button to rewatch a page',
'settings-error-anonbot': 'Error message for invalid settings',
'settings-error-anonminor': 'Error message for invalid settings',
'settings-error-badsites': 'Error message for invalid settings; $1 = bad sites',
'settings-error-nochanges': 'Error message for invalid settings',
'settings-error-nosites': 'Error message for invalid settings',
'show-edits': 'Label for the option to show edits',
'show-logEntries': 'Label for the option to show log entries',
'show-newPages': 'Label for the option to show new pages',
'siteList': 'Label for user defined site list',
'switch-dev': 'Label for switching to the development version of the script',
'switch-stable': 'Label for switching to the stable version of the script',
'title-globalWatchlist': '"Global watchlist" page title',
'title-globalWatchlistSettings': 'Settings page title',
'unwatch': 'Label for button to unwatch a page',
},
units: {
'asOf': 36,
'changesFeed': 19,
'emptyFeed': 20,
'filter-anon': 9,
'filter-bot': 8,
'filter-either': 11,
'filter-not-anon': 15,
'filter-not-bot': 13,
'filter-not-minor': 17,
'filter-only-anon': 14,
'filter-only-bot': 12,
'filter-only-minor': 16,
'globalWatchlistBackLink': 40,
'globalWatchlistLink': 1,
'globalWatchlistSettingsLink': 4,
'heading-globalWatchlist': 3,
'heading-globalWatchlistSettings': 6,
'markSeen': 24,
'markSeen-all': 28,
'markSeen-allConfirm': 37,
'notify-redirecting': 46,
'notify-savingFailed': 41,
'option-confirmAllSites': 43,
'option-fastMode': 44,
'otherOptions': 47,
'refresh': 23,
'resetChanges': 18,
'rewatch': 35,
'settings-error-anonbot': 48,
'settings-error-anonminor': 49,
'settings-error-badsites': 50,
'settings-error-nochanges': 51,
'settings-error-nosites': 52,
'show-edits': 32,
'show-logEntries': 34,
'show-newPages': 33,
'siteList': 21,
'switch-dev': 39,
'switch-stable': 38,
'title-globalWatchlist': 2,
'title-globalWatchlistSettings': 5,
'unwatch': 31,
},
bn: { // eslint-disable-line sort-keys
'asOf': '$1 অনুযায়ী',
'changesFeed': 'পরিবর্তনসহ সাইটগুলি',
'emptyFeed': 'অপরিবর্তিত সাইটগুলি',
'filter-anon': 'বেনামীর সম্পাদনাগুলি',
'filter-bot': 'বটের সম্পাদনাগুলি',
'filter-either': 'উভয়',
'filter-not-anon': 'শুধুমাত্র প্রবেশকৃত ব্যবহারকারীর দ্বারা সম্পাদনা',
'filter-not-bot': 'শুধুমাত্র বট-নয় সম্পাদনা',
'filter-not-minor': 'শুধুমাত্র অ-অনুল্লেখ্য সম্পাদনা',
'filter-only-anon': 'শুধুমাত্র বেনামে করা সম্পাদনা',
'filter-only-bot': 'শুধুমাত্র বটের সম্পাদনা',
'filter-only-minor': 'শুধুমাত্র অনুল্লেখ্য সম্পাদনা',
'globalWatchlistBackLink': 'বৈশ্বিক নজরতালিকায় ফেরত যান',
'globalWatchlistLink': 'বৈশ্বিক নজরতালিকা',
'globalWatchlistSettingsLink': 'সেটিংস',
'heading-globalWatchlist': 'বৈশ্বিক নজরতালিকা',
'heading-globalWatchlistSettings': 'বৈশ্বিক নজরতালিকার সেটিংস',
'markSeen': 'দেখা হিসাবে চিহ্নিত করুন',
'markSeen-all': 'সব সাইট দেখা হয়েছে হিসাবে চিহ্নিত করুন',
'markSeen-allConfirm': 'আপনি কি নিশ্চিত যে সব সাইটগুলিকে দেখা হয়েছে হিসাবে চিহ্নিত করতে চান?',
'notify-redirecting': 'বৈশ্বিক নজরতালিকায় পুনর্নির্দেশ করা হচ্ছে...',
'notify-savingFailed': 'সেটিংস সংরক্ষণ করতে ব্যর্থ। আবার চেষ্টা করুন',
'option-confirmAllSites': 'সমস্ত সাইট দেখা হয়েছে হিসেবে চিহ্নিত করার আগে নিশ্চিতকরণ প্রয়োজন',
'option-fastMode': 'কম বিশদসহ দ্রুত লোড হয়',
'otherOptions': 'অন্যান্য সেটিংস',
'refresh': 'পুনঃসতেজ করুন',
'resetChanges': 'পরিবর্তনগুলিকে পুনঃস্থাপিত করুন',
'rewatch': 'পুনরায় নজর রাখুন',
'settings-error-anonbot': 'বেনামী ব্যবহারকারীরা বট সম্পাদনা করতে পারবে না',
'settings-error-anonminor': 'বেনামী ব্যবহারকারীরা অনুল্লেখ্য সম্পাদনা করতে পারবে না',
'settings-error-badsites': 'অবৈধ সাইট(গুলি): $1',
'settings-error-nochanges': 'কোনও পরিবর্তনের ধরন নির্বাচন করা হয়নি',
'settings-error-nosites': 'কোনও সাইট নির্বাচন করা হয়নি',
'show-edits': 'পৃষ্ঠা সম্পাদনাগুলি দেখান',
'show-logEntries': 'লগ ভুক্তিগুলি দেখান',
'show-newPages': 'নতুন পৃষ্ঠাগুলি দেখান',
'siteList': 'ব্যবহারকারীর সংজ্ঞায়িত সাইটতালিকা',
'switch-dev': 'বিকাশ সংস্করণ ব্যবহার করুন',
'switch-stable': 'স্থিতিশীল সংস্করণ ব্যবহার করুন',
'title-globalWatchlist': 'বৈশ্বিক নজরতালিকা',
'title-globalWatchlistSettings': 'বৈশ্বিক নজরতালিকার সেটিংস',
'unwatch': 'নজর সরিয়ে নিন',
},
da: {
'globalWatchlistSettingsLink': 'Indstillinger',
'markSeen': 'Marker som set',
'markSeen-allConfirm': 'Er du sikker på, du vil markere sider som set?',
'otherOptions': 'Andre indstillinger',
'refresh': 'Genindlæs',
'rewatch': 'Gense',
'settings-error-anonbot': 'Anonyme brugere kan ikke lave bot redigeringer',
'settings-error-anonminor': 'Anonyme brugere kan ikke lave mindre redigeringer',
'settings-error-badsites': 'Ugyldig(e) side(r): $1',
'show-newPages': 'Vis nye sider',
},
de: {
'asOf': 'Stand $1',
'filter-anon': 'Anonyme Bearbeitungen',
'filter-bot': 'Bot-Bearbeitungen',
'filter-not-anon': 'Nur Bearbeitungen von angemeldeten Benutzern',
'filter-not-bot': 'Nur Nicht-Bot-Bearbeitungen',
'filter-not-minor': 'Nur geringfügige Bearbeitungen',
'filter-only-anon': 'Nur anonym gemachte Bearbeitungen',
'filter-only-bot': 'Nur Nicht-Bot-Änderungen',
'filter-only-minor': 'Nur geringfügige Bearbeitungen',
'globalWatchlistLink': 'Globale Beobachtungsliste',
'globalWatchlistSettingsLink': 'Einstellungen',
'heading-globalWatchlist': 'Globale Beobachtungsliste',
'heading-globalWatchlistSettings': 'Globale Beobachtungslisteneinstellungen',
'markSeen': 'Als gesehen markieren',
'option-fastMode': 'Lade schneller, zulasten des Detailgrads',
'otherOptions': 'Andere Einstellungen',
'refresh': 'Aktualisieren',
'resetChanges': 'Gemachte Änderungen zurücksetzen',
'settings-error-anonbot': 'Anonyme Benutzer können keine Bot-Bearbeitungen machen',
'settings-error-anonminor': 'Anonyme Benutzer können keine geringfügigen Bearbeitungen machen',
'settings-error-nochanges': 'Keine Änderungstypen ausgewählt',
'show-edits': 'Zeige Seitenbearbeitungen',
'show-logEntries': 'Zeige Log-Einträge',
'show-newPages': 'Zeige neue Seiten',
'switch-dev': 'Zur Entwicklungsversion wechseln',
'switch-stable': 'Zu stabiler Version wechseln',
'title-globalWatchlist': 'Globale Beobachtungsliste',
'title-globalWatchlistSettings': 'Globale Beobachtungslisteneinstellungen',
},
es: {
'settings-error-anonminor': 'Usuarios anónimos no pueden hacer ediciones menores',
},
fr: {
'asOf': 'Le $1',
'changesFeed': 'Sites modifiés',
'emptyFeed': 'Sites inchangés',
'filter-anon': 'Contributions anonymes',
'filter-bot': 'Modifications de robot',
'filter-either': 'Toutes',
'filter-not-anon': 'Contributions d\'utilisateurs connectés',
'filter-not-bot': 'Modifications faites par des contributeurs',
'filter-not-minor': 'Modifications non mineures',
'filter-only-anon': 'Contributions anonymes',
'filter-only-bot': 'Modifications par des outils automatisés',
'filter-only-minor': 'Modifications mineures',
'globalWatchlistBackLink': 'Retour à la liste de suivi globale',
'globalWatchlistLink': 'Liste de suivi globale',
'globalWatchlistSettingsLink': 'Réglages',
'heading-globalWatchlist': 'Liste de suivi globale',
'heading-globalWatchlistSettings': 'Réglages liste de suivi globale',
'markSeen': 'Marquer comme vu',
'markSeen-all': 'Marquer tous les sites comme vus',
'markSeen-allConfirm': 'Êtes-vous certain(e) de vouloir marquer les sites comme vus ?',
'notify-redirecting': 'Retour vers la liste de suivi globale en cours ...',
'notify-savingFailed': 'Échec de l\'enregistrement des paramètres. Merci de réessayer',
'option-confirmAllSites': 'Demande une confirmation avant de marquer tous les sites comme vus',
'option-fastMode': 'Chargement plus rapide, avec moins de détails',
'otherOptions': 'Autres options',
'refresh': 'Rafraîchir',
'resetChanges': 'Réinitialiser',
'settings-error-anonbot': 'Les contributeurs anonymes ne peuvent pas faire d\'éditions automatiques',
'settings-error-anonminor': 'Les contributeurs anonymes ne peuvent pas faire d\'éditions mineures',
'settings-error-badsites': 'Site(s) non valide(s) : $1',
'settings-error-nosites': 'Aucun site sélectionné',
'show-newPages': 'Affiche les nouvelles pages',
'siteList': 'Liste personnalisée',
'switch-dev': 'Passer à la version de développement',
'switch-stable': 'Passer à la version stable',
'title-globalWatchlist': 'Liste de suivi globale',
'title-globalWatchlistSettings': 'Réglages liste de suivi globale',
'unwatch': 'Ne plus suivre',
},
gu: {
'filter-only-minor': 'ખાલી નાનાં સંપાદનો',
'globalWatchlistSettingsLink': 'સેટિંગ્સ',
'resetChanges': 'સેટિંગ્સ ફરીથી સેટ કરો',
'unwatch': 'ચોકી રાખવાનું બંધ કરો',
},
he: {
'asOf': 'נכון ל־$1',
'changesFeed': 'אתרים עם שינויים',
'emptyFeed': 'אתרים ללא שינויים',
'filter-anon': 'עריכות אלמונים',
'filter-bot': 'עריכות בוט',
'filter-either': 'שניהם',
'filter-not-anon': 'רק עריכות של משתמשים מחיברים',
'filter-not-bot': 'רק עריכות של לא בוטים',
'filter-not-minor': 'רק עריכות לא משניות',
'filter-only-anon': 'רק עריכות של אלמונים',
'filter-only-bot': 'רק עריכות של בוטים',
'filter-only-minor': 'רק עריכות משניות',
'globalWatchlistBackLink': 'חזרה לרשימת מעקב גלובלית',
'globalWatchlistLink': 'רשימת מעקב גלובלית',
'globalWatchlistSettingsLink': 'הגדרות',
'heading-globalWatchlist': 'רשימת מעקב גלובלית',
'heading-globalWatchlistSettings': 'הגדרות רשימת מעקב גלובלית',
'markSeen': 'לסמן בתור אתרים שנצפו',
'markSeen-all': 'לסמן את כל האתרים כאתרים שנצפו',
'markSeen-allConfirm': 'אתם בטוחים כי ברצונכם לסמן אותם בתור אתרים שנצפו?',
'notify-redirecting': 'הפניה חזרה לרשימת מעקב גלובלית...',
'notify-savingFailed': 'שמירת ההגדרות נכשלה. נא לנסות שוב',
'option-confirmAllSites': 'לדרוש אישור לפני סימון כל האתרים כאתרים שנצפו',
'option-fastMode': 'לטעון מהר יותר ולוותר על חלק מהפרטים',
'otherOptions': 'הגדרות אחרות',
'refresh': 'רענון',
'resetChanges': 'איפוס השינויים שנעשו',
'rewatch': 'לעקוב שוב',
'settings-error-anonbot': 'משתמשים אלמוניים אינם יכולים לערוך כבוטים',
'settings-error-anonminor': 'משתמשים אלמוניים אינם יכולים לעשות עריכות משניות',
'settings-error-badsites': 'אתרים בלתי־תקינים: $1',
'settings-error-nochanges': 'לא נבחרו סוגי עריכות',
'settings-error-nosites': 'לא נבחרו אתרים',
'show-edits': 'הצגת עריכות דף',
'show-logEntries': 'הצגת רשומות יומן',
'show-newPages': 'הצגת דפים חדשים',
'siteList': 'רשימת האתרים להצגה',
'switch-dev': 'מעבר לגרסת פיתוח',
'switch-stable': 'מעבר לגרסה יציבה',
'title-globalWatchlist': 'רשימת מעקב גלובלית',
'title-globalWatchlistSettings': 'הגדרות רשימת מעקב גלובלית',
'unwatch': 'להפסיק לעקוב',
},
hi: {
'filter-anon': 'बेनाम संपादन',
'filter-bot': 'बाट संपादन',
'filter-either': 'या तो',
'filter-not-bot': 'केवल नॉन-बाट संपादन',
'filter-only-anon': 'केवल बेनाम किए गए संपादन',
'filter-only-bot': 'केवल बाट संपादन',
'globalWatchlistLink': 'वैश्विक ध्यानसूची',
'globalWatchlistSettingsLink': 'सेटिंग्स',
'heading-globalWatchlist': 'वैश्विक ध्यानसूची',
'heading-globalWatchlistSettings': 'वैश्विक ध्यानसूची सेटिंग्स',
'title-globalWatchlist': 'वैश्विक ध्यानसूची',
'title-globalWatchlistSettings': 'वैश्विक ध्यानसूची सेटिंग्स',
},
it: {
'changesFeed': 'Siti con modifiche',
'emptyFeed': 'Siti senza modifiche',
'filter-anon': 'Modifiche anonime',
'filter-bot': 'Modifiche di bot',
'filter-either': 'Entrambi',
'filter-not-anon': 'Solo modifiche fatte da utenti registrati',
'filter-not-bot': 'Solo modifiche non bot',
'filter-not-minor': 'Solo modifiche non minori',
'filter-only-anon': 'Solo modifiche fatte da anonimi',
'filter-only-bot': 'Solo modifiche bot',
'filter-only-minor': 'Solo modifiche minori',
'globalWatchlistSettingsLink': 'Impostazioni',
'otherOptions': 'Altre impostazioni',
'refresh': 'Aggiorna',
},
ja: {
'settings-error-anonbot': '匿名利用者はボットを操作する編集が認められていません',
'settings-error-badsites': '無効なサイト: $1',
'settings-error-nochanges': '変更の種類を選択していません',
'settings-error-nosites': 'サイトを選択していません',
},
lt: {
'otherOptions': 'Kitos nuostatos',
},
mk: {
'otherOptions': 'Други поставки',
},
ml: {
'asOf': '$1 പ്രകാരം',
'changesFeed': 'മാറ്റങ്ങളുള്ള സൈറ്റുകൾ',
'emptyFeed': 'മാറ്റങ്ങളിലാത്ത സൈറ്റുകൾ',
'filter-bot': 'ബോട്ട് തിരുത്തലുകൾ',
'filter-not-anon': 'പ്രവേശിച്ച ഉപയോക്താക്കൾ മാത്രം നടത്തിയ തിരുത്തലുകൾ',
'filter-not-bot': 'ബോട്ട് ഉപയോഗിക്കാതെ നടത്തിയ തിരുത്തലുകൾ',
'filter-only-bot': 'ബോട്ട് തിരുത്തലുകൾ മാത്രം',
'globalWatchlistSettingsLink': 'സജ്ജീകരണങ്ങൾ',
'markSeen-all': 'എല്ലാ സൈറ്റുകളും കണ്ടതായി അടയാളപ്പെടുത്തുക',
'markSeen-allConfirm': 'സൈറ്റുകൾ കണ്ടതായി അടയാളപ്പെടുത്താൻ തയ്യാറാണോ?',
'otherOptions': 'മറ്റ് സജ്ജീകരണങ്ങൾ',
'refresh': 'പുതുക്കുക',
'settings-error-badsites': 'അസാധുവായ സൈറ്റു(കൾ):$1',
'settings-error-nosites': 'ഒരു സൈറ്റും തിരഞ്ഞെടുത്തിട്ടില്ല',
'show-edits': 'താളിലെ തിരുത്തലുകൾ കാണിക്കുക',
'show-newPages': 'പുതിയ താളുകൾ കാണിക്കുക',
},
nl: {
'asOf': 'Sinds $1',
'changesFeed': 'Veranderde projecten',
'emptyFeed': 'Ongewijzigde projecten',
'filter-anon': 'Anonieme bewerkingen',
'filter-bot': 'Botbijdragen',
'filter-either': 'Een van beide',
'filter-not-anon': 'Slechts bewerkingen door aangemelde gebruikers',
'filter-not-bot': 'Alleen niet-botbewerkingen',
'filter-not-minor': 'Geen kleine bewerkingen',
'filter-only-anon': 'Alleen anonieme bijdragen',
'filter-only-bot': 'Alleen botbewerkingen',
'filter-only-minor': 'Alleen kleine bewerkingen',
'globalWatchlistBackLink': 'Terug naar de wereldwijde volglijst',
'globalWatchlistLink': 'Wereldwijde volglijst',
'globalWatchlistSettingsLink': 'Instellingen',
'heading-globalWatchlist': 'Wereldwijde volglijst',
'heading-globalWatchlistSettings': 'Instellingen wereldwijde volglijst',
'markSeen': 'Markeren als gezien',
'markSeen-all': 'Markeer alle projecten als gezien',
'markSeen-allConfirm': 'Weet u zeker dat u de projecten als gezien wilt markeren?',
'notify-redirecting': 'U wordt terugverwezen naar de wereldwijde volglijst...',
'notify-savingFailed': 'Instellingen niet opgeslagen. Probeer alstublieft opnieuw',
'option-confirmAllSites': 'Eis bevestiging alvorens alle projecten als gezien te markeren',
'option-fastMode': 'Snel laden met als nadeel minder gedetailleerd',
'otherOptions': 'Andere instellingen',
'refresh': 'Ververs',
'resetChanges': 'Herstel gedane veranderingen',
'rewatch': 'Bekijk opieuw',
'settings-error-anonbot': 'Anoniemen kunnen geen botbewerkingen doen',
'settings-error-anonminor': 'Anoniemen kunnen geen kleine bewerkingen doen',
'settings-error-badsites': 'Ongeldig(e) project(en): $1',
'settings-error-nochanges': 'Geen soorten verandering geselecteerd',
'settings-error-nosites': 'Geen projecten geselecteerd',
'show-edits': 'Toon paginabewerkingen',
'show-logEntries': 'Toon logboekregels',
'show-newPages': 'Toon nieuwe pagina\'s',
'siteList': 'Door gebruiker samengestelde projectenlijst',
'switch-dev': 'Overschakelen op ontwikkelversie',
'switch-stable': 'Overschakelen op productieversie',
'title-globalWatchlist': 'Wereldwijde volglijst',
'title-globalWatchlistSettings': 'Instellingen wereldwijde volglijst',
'unwatch': 'Niet meer volgen',
},
pt: {
'notify-redirecting': 'A redirecionar da lista global de \'a vigiar\'...',
'show-edits': 'Mostrar edições de página',
'show-logEntries': 'Mostrar entradas do registo',
'show-newPages': 'Mostrar novas páginas',
},
ru: {
'asOf': 'По состоянию на $1',
'changesFeed': 'Проекты с новыми изменениями',
'emptyFeed': 'Проекты без изменений',
'filter-anon': 'Анонимные правки',
'filter-bot': 'Правки ботов',
'filter-either': 'Ни то ни то',
'filter-not-anon': 'Исключить анонимные правки',
'filter-not-bot': 'Исключить правки ботов',
'filter-not-minor': 'Исключить малые правки',
'filter-only-anon': 'Только анонимные правки',
'filter-only-bot': 'Только правки ботов',
'filter-only-minor': 'Только малые правки',
'globalWatchlistBackLink': 'Назад к глобальному списку наблюдения',
'globalWatchlistLink': 'Глобальный список наблюдения',
'globalWatchlistSettingsLink': 'Настройки',
'heading-globalWatchlist': 'Глобальный список наблюдения',
'heading-globalWatchlistSettings': 'Настройки глобального СН',
'markSeen': 'Пометить прочитанным',
'markSeen-all': 'Пометить всё прочитанным',
'markSeen-allConfirm': 'Вы уверены, что хотите пометить все проекты как прочитанные?',
'notify-redirecting': 'Возврат к глобальному списку наблюдения…',
'notify-savingFailed': 'Не удалось сохранить настройки. Попробуйте ещё раз',
'option-confirmAllSites': 'Требовать подтверждения перед тем, как помечать все проекты прочитанными',
'option-fastMode': 'Загружаться быстрее, но выдавать менее детализованные результаты',
'otherOptions': 'Другие настройки',
'refresh': 'Обновить',
'resetChanges': 'Сбросить изменения',
'rewatch': 'Следить',
'settings-error-anonbot': 'Нельзя править из-под аккаунта бота анонимно',
'settings-error-anonminor': 'Нельзя помечать правки как малые анонимно',
'settings-error-badsites': 'Проекты с ошибками в названии: $1',
'settings-error-nochanges': 'Выберите хотя бы один вид изменений',
'settings-error-nosites': 'Выберите хотя бы один проект',
'show-edits': 'Показать правки на странице',
'show-logEntries': 'Показать журнал',
'show-newPages': 'Показать новые страницы',
'siteList': 'Пользовательский список проектов',
'switch-dev': 'Переключиться в меню разработчика',
'switch-stable': 'Переключиться в стабильную версию',
'title-globalWatchlist': 'Глобальный список наблюдения',
'title-globalWatchlistSettings': 'Настройки глобального СН',
'unwatch': 'Не следить',
},
zh: {
'asOf': '从$1起',
'changesFeed': '有变化的网站',
'emptyFeed': '没有变化的网站',
'filter-anon': '匿名编辑',
'filter-bot': '机器人编辑',
'filter-either': '两者任一',
'filter-not-anon': '仅由登录用户进行的编辑',
'filter-not-bot': '仅列出非机器人编辑',
}
};
});
mw.loader.using([ 'jquery.makeCollapsible', 'mediawiki.util', 'mediawiki.api', 'mediawiki.ForeignApi', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows', 'oojs-ui.styles.icons-movement', 'oojs-ui.styles.icons-interactions', 'oojs-ui.styles.icons-content', 'oojs-ui.styles.icons-media', 'oojs-ui.styles.icons-moderation' ], () => {
$(document).ready(() => {
if (mw.config.get('wgNamespaceNumber') === -1) {
const page = mw.config.get('wgCanonicalSpecialPageName');
if (page === 'Blankpage') {
const page2 = mw.config.get('wgTitle').split('/');
if (page2[1]) {
if (page2[1] === 'GlobalWatchlist') {
GlobalWatchlist.init(1);
}
else if (page2[1] === 'GlobalWatchlistConfig' && mw.config.get('wgDBname') === 'metawiki') {
GlobalWatchlist.init(2);
}
}
} else if (page === 'Watchlist') {
GlobalWatchlist.init(3);
}
}
});
});
// </nowiki>