From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/* jshint maxerr: 999 */



// <nowiki>

$.when(

	mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.user', 

		'ext.gadget.morebits', 'mediawiki.widgets.DateInputWidget', 'moment']),

	$.ready

).then(function() {





var WPing = {};

window.WPing = WPing;



WPing.api = new mw.Api({

	ajax: { headers: { 'Api-User-Agent': '[[w:en:User:SD0001/W-Ping.js]]' } }

});



WPing.pingDialog = function pingDialog(page) {

	var Window = new Morebits.simpleWindow(800, 500);

	Window.setScriptName('W-Ping');

	Window.setTitle("Schedule a watchlist ping for " + page);

	Window.addFooterLink('Upcoming pings', 'Special:BlankPage/W-Ping');

	Window.addFooterLink('W-Ping', 'User:SD0001/W-Ping');



	var form = new Morebits.quickForm(WPing.evaluate);



	var reason = '';

	var date = moment().utcOffset(WPing.getUserTimeZone()).format('YYYY-MM-DD');



	// See if there's already a scheduled ping for this page, if so override the above defaults

	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (opt && optpage]) {

		reason = optpage][1];

		date = moment(optpage][0 * 60000).utcOffset(WPing.getUserTimeZone()).format('YYYY-MM-DD');

	}



	form.append({

		type: 'input',

		label: 'Reason: ',

		name: 'reason',

		value: reason,

		size: '100px'

	});



	// input field replaced by datepicker after render

	form.append({

		type: 'input',

		name: 'date',

		label: 'Ping on: ',

	});



	form.append({

		type: 'hidden',

		name: 'page',

		value: page

	});



	if (opt && optpage && mw.config.get('wgCanonicalSpecialPageName') !== 'Watchlist') {

		form.append({

			type: 'button',

			label: 'Cancel ping',

			style: 'margin-top: 5px',

			event: function cancelPing() {

				Morebits.status.init(result);

				Morebits.simpleWindow.setButtonsEnabled(false);

				var status = new Morebits.status('Ping', 'Cancelling', 'status');



				delete optpage];

				WPing.updatePingList(opt).then(function() {

					status.info('Done');

					mw.track('counter.gadget_WPing.ping_cancelled');

					window.setTimeout(function() {

						Window.close(); // close dialog

					}, 300);

				}).catch(function(err) {

					status.error('Failed to cancel: ' + JSON.stringify(err));

				});

			}

		});

	}



	form.append({ type: 'submit', label: 'Submit' });



	var result = form.render();

	Window.setContent(result);

	Window.display();

	mw.track('counter.gadget_WPing.dialog_opened');



	var datepicker = new mw.widgets.DateInputWidget({

		name: 'date',

		value: date

	});

	datepicker.setRequired(true);

	$(result.date).replaceWith(datepicker.$element);



	// prevent datepicker from getting hidden into the dialog

	$(Window.content).parent().css('overflow', 'visible');

	$(Window.content).css('overflow', 'visible');



	datepicker.$element.find('label').css({

		'display': 'block',

		'font-size': '110%'

	});



	// prevent enter in date field from submitting

	// leads to surprises if date was invalid, as datepicker takes in a close valid date anyway

	datepicker.$element.find('input[type=text]').keypress(function(e) {

		if (e.keyCode === 13) {

			e.preventDefault();

			return false;

		}

	});



	var durations = Array.isArray(window.WPing_Quick_Durations) ?

		window.WPing_Quick_Durations :

		 '1 day', '3 days', '1 week', '2 weeks', '1 month' ];



	var $quickSelect = $('<span>');

	durations.forEach(function(e) {

		$('<a>').addClass('wping-prompt').text(e).appendTo($quickSelect);

	});

	datepicker.$element.after($quickSelect);



	$quickSelect.find('a').css({

		'padding': '0 5px 0 5px'

	}).click(function(e) {

		e.preventDefault();



		// moment doesn't natively parse durations such as "3 weeks", so we manually separate

		// the number and the unit, and give it as moment.duration(3, "weeks")

		var s = e.target.textContent;

		var i;

		for (i = 0; i < s.length; i++) {

			if (si < '0' || si > '9') {

				break;

			}

		}

		var num = parseInt(s);

		var text = s.slice(i).trim();

		var duration = moment.duration(num, text);



		var targetdate = moment().add(duration);

		datepicker.setValue(targetdate.utcOffset(WPing.getUserTimeZone()).format('YYYY-MM-DD'));

	});



};



WPing.evaluate = function evaluate(e) {

	var form = e.target;



	var page = form.page.value;

	var reason = form.reason.value;



	// moment reads the date as if it is in the system time zone, we apply an offset correction

	// to account for the case when it's differnt from the time zone in user preferences

	var userzone = WPing.getUserTimeZone();

	var syszone = -new Date().getTimezoneOffset();

	var enteredDate = moment(form.date.value, 'YYYY-MM-DD').add(syszone - userzone, 'minutes');



	// add hours and minutes to entered date:

	var now = moment().utcOffset(WPing.getUserTimeZone());

	var pingAt = enteredDate.add(now.hours(), 'hours').add(now.minutes(), 'minutes');



	// store pingtime as number of minutes past unix epoch (to optimise storage)

	var pingtime = parseInt(pingAt.unix() / 60);



	Morebits.status.init(form);

	Morebits.simpleWindow.setButtonsEnabled(false);



	var status = new Morebits.status('Ping', 'Scheduling', 'status');



	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (!opt) {  // for the first-time user

		opt = {};

	}

	optpage =  pingtime, reason ];



	WPing.updatePingList(opt).then(function() {

		status.info('Done');

		mw.track('counter.gadget_WPing.ping_saved');



		// automatically close window in a short while

		window.setTimeout(function() {

			$(form).parent().prev().find('.ui-dialog-titlebar-close').click();

		}, 300);



		// while snoozing, remove the ping entry

		if (mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist') {

			WPing.removePingDisplayLine(page);

			if (page !== Morebits.pageNameNorm) {

                mw.track('counter.gadget_WPing.ping_snoozed');

            }

		}

	}).catch(function(err) {

		status.error('Failed ' + JSON.stringify(err));

	});

};



WPing.attachPings = function attachPings() {

	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (!opt) return;



	var $ul = $('<ul>').css({

		'margin-left': 'calc((6px + 3px) * 5 + 0.35714286em)'  // to match that of .mw-changeslist ul

	});



	var pingPages = [];

	$.each(opt, function(page, tr) {

		var pingtime = tr0 * 60000;

		if (new Date().getTime() > pingtime) {

			pingPages.push(page);



			// render wikilinks in reason text, though all links will appear blue

			var reason = tr1].replace(/\[\[:?(?:([^\|\]]+?)\|)?([^\]\|]+?)\]\]/g, function(_, target, text) {

				if (!target) {

					target = text;

				}

				return '<a href="' + mw.util.getUrl(target) + '" title="' + target + '">' + text + '</a>';

			});

			

			var histlink = mw.Title.newFromText(page).namespace < 0 ? 'hist' : 

				('<a href="' + mw.util.getUrl(page, { action: 'history' }) + '">hist</a>');



			$('<li>').addClass('wping-line').attr('data-page', page).html(

				'(' + histlink + ') ' +

				'<span class="mw-changeslist-separator"></span> ' +

				'<a href="' + mw.util.getUrl(page) + '" title="' + page + '">' + page + '</a> ' +

				'<span class="mw-changeslist-separator"></span> ' +

				(reason ? '<i>(' + reason + ')</i> <span class="mw-changeslist-separator"></span> ' : '') +

				'[ <a href=# class="wping-snooze">snooze</a> | <a href=# class="wping-dismiss">dismiss</a> ]'

			).appendTo($ul);

		}

	});



	if (!pingPages.length) {

		return;

	}



	var $element = $('.mw-rcfilters-ui-changesListWrapperWidget').length ?

		$('.mw-rcfilters-ui-changesListWrapperWidget') :

		( $('.mw-changeslist').length ? // for users of non-AJAX watchlist

			$('.mw-changeslist') :

			$('.mw-changeslist-empty') );



	$element.before(

		$('<div>').attr('id', 'wping').append(

			$('<h4>').text('Pings'),

			$ul

		)

	);



	// check if pinged pages exists, if not turn the links red, occurs lazily

	// XXX: only works if there are <50 pages

	WPing.api.get({ titles: pingPages }).then(function(json) {

		$.each(Object.values(json.query.pages), function(pageid, data) {

			if (data.missing === '') {

				$ul.find('a[href="' + mw.util.getUrl(data.title) + '"]').addClass('new');

			}

		});

	});



	$ul.find('.wping-snooze').click(function(e) {

		e.preventDefault();

		var page = $(e.target).parent().data('page');

		WPing.pingDialog(page);

	});



	$ul.find('.wping-dismiss').click(function(e) {

		e.preventDefault();

		var page = $(e.target).parent().data('page');



		delete optpage];

		WPing.updatePingList(opt);

		WPing.removePingDisplayLine(page);

		mw.track('counter.gadget_WPing.ping_dismissed');

	});



};



WPing.updatePingList = function(opt) {

	var optString = JSON.stringify(opt);



	// update object locally too, so that it can be retrieved in case user wants to change reason/date

	// again (before page is reloaded)

	mw.user.options.set('userjs-wping-list', optString);



	return WPing.api.saveOption('userjs-wping-list', optString);

};



WPing.removePingDisplayLine = function removePingDisplayLine(page) {

	$('#wping ul li[data-page="' + $.escapeSelector(page) + '"]').remove();

	if ($('#wping ul').children().length === 0) {

		$('#wping').remove();

	}

};



WPing.buildSpecialPage = function buildSpecialPage() {

	$('#firstHeading').text('Upcoming watchlist pings');

	document.title = 'Upcoming watchlist pings';

	$('#mw-content-text').empty();



	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (!opt) {

		opt = {};

	}

	var timezone = WPing.getUserTimeZone();



	var $ul = $('<ul>');

	$.each(opt, function(page, tr) {

		var time = new Date(tr0 * 60000);



		// render wikilinks in reason text, though all links will appear blue

		var reason = tr1].replace(/\[\[:?(?:([^\|\]]+?)\|)?([^\]\|]+?)\]\]/g, function(_, target, text) {

			if (!target) {

				target = text;

			}

			return '<a href="' + mw.util.getUrl(target) + '" title="' + target + '">' + text + '</a>';

		});

		$ul.append(

			$('<li>').html(

				'<a href="' + mw.util.getUrl(page) + '" title="' + page + '">' + page + '</a>: ' +

				(reason ? '(' + reason + ') ' : '') +

				moment(time).utcOffset(timezone).format('HH:mm, D MMMM YYYY')

			)

		);

	});



	$('#mw-content-text').append(

		$('<p>').text('A ping shall be delivered to your watchlist for the following pages, at the specified time in ' + WPing.getTimeZoneString(timezone) + ' time zone:'),

		$ul

	);



	WPing.api.get({ titles: Object.keys(opt) }).then(function(json) {

		$.each(Object.values(json.query.pages), function(pageid, data) {

			if (data.missing === '') {

				$ul.find('a[href="' + mw.util.getUrl(data.title) + '"]').addClass('new');

			}

		});

	});



};



WPing.getUserTimeZone = function() {

	if (WPing.userTimeZone) { // cache it

		return WPing.userTimeZone;

	}

	switch (window.WPing_timezone || 'preferences') {

		case 'utc':

			WPing.userTimeZone = 0;

			break;

		case 'system':

			WPing.userTimeZone = -new Date().getTimezoneOffset();

			break;

		case 'preferences':

			WPing.userTimeZone = parseInt(mw.user.options.get('timecorrection').split('|')[1]);

			break;

	}

	return WPing.userTimeZone;

};



WPing.getTimeZoneString = function(timecorrection) {

	var negative = false;

	if (timecorrection < 0) {

		timecorrection = -timecorrection;

		negative = true;

	}

	var hourCorrection = parseInt(timecorrection/60);

	hourCorrection = (hourCorrection < 10 ? '0' : '') + hourCorrection.toString();



	var minuteCorrection = timecorrection % 60;

	minuteCorrection = (minuteCorrection < 10 ? '0' : '') + minuteCorrection.toString();



	return 'UTC' + (negative ? '–' : '+') + hourCorrection + minuteCorrection;

};



// SET UP

if (mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist') {

	WPing.attachPings();

} else if (mw.config.get('wgPageName') === 'Special:BlankPage/W-Ping') {

	WPing.buildSpecialPage();

} else {

	var pageName = Morebits.pageNameNorm;

	// for Special:Log views where the form at the top of the page was used:

	if (pageName === 'Special:Log') {

		var user = mw.util.getParamValue('user');

		var type = mw.util.getParamValue('type');

		if (type) {

			pageName += '/' + type;

		}

		if (user) {

			pageName += '/' + Morebits.string.toUpperCaseFirstChar(user);

		}

	} else if (pageName === 'Special:Contributions') {

		var user = mw.util.getParamValue('user');

		if (user) {

			pageName += '/' + Morebits.string.toUpperCaseFirstChar(user);

		}

	}

	if (pageName) {

		var li = mw.util.addPortletLink('p-cactions', '#', 'W-Ping', 'ca-wping', 'Schedule a watchlist ping for this page');

		li.addEventListener('click', function(e) {

			e.preventDefault();

			WPing.pingDialog(pageName);

		});	

	}

}





}).catch(function(err) {

	console.error('[W-Ping]:', err);

});



// </nowiki>
From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/* jshint maxerr: 999 */



// <nowiki>

$.when(

	mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.user', 

		'ext.gadget.morebits', 'mediawiki.widgets.DateInputWidget', 'moment']),

	$.ready

).then(function() {





var WPing = {};

window.WPing = WPing;



WPing.api = new mw.Api({

	ajax: { headers: { 'Api-User-Agent': '[[w:en:User:SD0001/W-Ping.js]]' } }

});



WPing.pingDialog = function pingDialog(page) {

	var Window = new Morebits.simpleWindow(800, 500);

	Window.setScriptName('W-Ping');

	Window.setTitle("Schedule a watchlist ping for " + page);

	Window.addFooterLink('Upcoming pings', 'Special:BlankPage/W-Ping');

	Window.addFooterLink('W-Ping', 'User:SD0001/W-Ping');



	var form = new Morebits.quickForm(WPing.evaluate);



	var reason = '';

	var date = moment().utcOffset(WPing.getUserTimeZone()).format('YYYY-MM-DD');



	// See if there's already a scheduled ping for this page, if so override the above defaults

	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (opt && optpage]) {

		reason = optpage][1];

		date = moment(optpage][0 * 60000).utcOffset(WPing.getUserTimeZone()).format('YYYY-MM-DD');

	}



	form.append({

		type: 'input',

		label: 'Reason: ',

		name: 'reason',

		value: reason,

		size: '100px'

	});



	// input field replaced by datepicker after render

	form.append({

		type: 'input',

		name: 'date',

		label: 'Ping on: ',

	});



	form.append({

		type: 'hidden',

		name: 'page',

		value: page

	});



	if (opt && optpage && mw.config.get('wgCanonicalSpecialPageName') !== 'Watchlist') {

		form.append({

			type: 'button',

			label: 'Cancel ping',

			style: 'margin-top: 5px',

			event: function cancelPing() {

				Morebits.status.init(result);

				Morebits.simpleWindow.setButtonsEnabled(false);

				var status = new Morebits.status('Ping', 'Cancelling', 'status');



				delete optpage];

				WPing.updatePingList(opt).then(function() {

					status.info('Done');

					mw.track('counter.gadget_WPing.ping_cancelled');

					window.setTimeout(function() {

						Window.close(); // close dialog

					}, 300);

				}).catch(function(err) {

					status.error('Failed to cancel: ' + JSON.stringify(err));

				});

			}

		});

	}



	form.append({ type: 'submit', label: 'Submit' });



	var result = form.render();

	Window.setContent(result);

	Window.display();

	mw.track('counter.gadget_WPing.dialog_opened');



	var datepicker = new mw.widgets.DateInputWidget({

		name: 'date',

		value: date

	});

	datepicker.setRequired(true);

	$(result.date).replaceWith(datepicker.$element);



	// prevent datepicker from getting hidden into the dialog

	$(Window.content).parent().css('overflow', 'visible');

	$(Window.content).css('overflow', 'visible');



	datepicker.$element.find('label').css({

		'display': 'block',

		'font-size': '110%'

	});



	// prevent enter in date field from submitting

	// leads to surprises if date was invalid, as datepicker takes in a close valid date anyway

	datepicker.$element.find('input[type=text]').keypress(function(e) {

		if (e.keyCode === 13) {

			e.preventDefault();

			return false;

		}

	});



	var durations = Array.isArray(window.WPing_Quick_Durations) ?

		window.WPing_Quick_Durations :

		 '1 day', '3 days', '1 week', '2 weeks', '1 month' ];



	var $quickSelect = $('<span>');

	durations.forEach(function(e) {

		$('<a>').addClass('wping-prompt').text(e).appendTo($quickSelect);

	});

	datepicker.$element.after($quickSelect);



	$quickSelect.find('a').css({

		'padding': '0 5px 0 5px'

	}).click(function(e) {

		e.preventDefault();



		// moment doesn't natively parse durations such as "3 weeks", so we manually separate

		// the number and the unit, and give it as moment.duration(3, "weeks")

		var s = e.target.textContent;

		var i;

		for (i = 0; i < s.length; i++) {

			if (si < '0' || si > '9') {

				break;

			}

		}

		var num = parseInt(s);

		var text = s.slice(i).trim();

		var duration = moment.duration(num, text);



		var targetdate = moment().add(duration);

		datepicker.setValue(targetdate.utcOffset(WPing.getUserTimeZone()).format('YYYY-MM-DD'));

	});



};



WPing.evaluate = function evaluate(e) {

	var form = e.target;



	var page = form.page.value;

	var reason = form.reason.value;



	// moment reads the date as if it is in the system time zone, we apply an offset correction

	// to account for the case when it's differnt from the time zone in user preferences

	var userzone = WPing.getUserTimeZone();

	var syszone = -new Date().getTimezoneOffset();

	var enteredDate = moment(form.date.value, 'YYYY-MM-DD').add(syszone - userzone, 'minutes');



	// add hours and minutes to entered date:

	var now = moment().utcOffset(WPing.getUserTimeZone());

	var pingAt = enteredDate.add(now.hours(), 'hours').add(now.minutes(), 'minutes');



	// store pingtime as number of minutes past unix epoch (to optimise storage)

	var pingtime = parseInt(pingAt.unix() / 60);



	Morebits.status.init(form);

	Morebits.simpleWindow.setButtonsEnabled(false);



	var status = new Morebits.status('Ping', 'Scheduling', 'status');



	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (!opt) {  // for the first-time user

		opt = {};

	}

	optpage =  pingtime, reason ];



	WPing.updatePingList(opt).then(function() {

		status.info('Done');

		mw.track('counter.gadget_WPing.ping_saved');



		// automatically close window in a short while

		window.setTimeout(function() {

			$(form).parent().prev().find('.ui-dialog-titlebar-close').click();

		}, 300);



		// while snoozing, remove the ping entry

		if (mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist') {

			WPing.removePingDisplayLine(page);

			if (page !== Morebits.pageNameNorm) {

                mw.track('counter.gadget_WPing.ping_snoozed');

            }

		}

	}).catch(function(err) {

		status.error('Failed ' + JSON.stringify(err));

	});

};



WPing.attachPings = function attachPings() {

	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (!opt) return;



	var $ul = $('<ul>').css({

		'margin-left': 'calc((6px + 3px) * 5 + 0.35714286em)'  // to match that of .mw-changeslist ul

	});



	var pingPages = [];

	$.each(opt, function(page, tr) {

		var pingtime = tr0 * 60000;

		if (new Date().getTime() > pingtime) {

			pingPages.push(page);



			// render wikilinks in reason text, though all links will appear blue

			var reason = tr1].replace(/\[\[:?(?:([^\|\]]+?)\|)?([^\]\|]+?)\]\]/g, function(_, target, text) {

				if (!target) {

					target = text;

				}

				return '<a href="' + mw.util.getUrl(target) + '" title="' + target + '">' + text + '</a>';

			});

			

			var histlink = mw.Title.newFromText(page).namespace < 0 ? 'hist' : 

				('<a href="' + mw.util.getUrl(page, { action: 'history' }) + '">hist</a>');



			$('<li>').addClass('wping-line').attr('data-page', page).html(

				'(' + histlink + ') ' +

				'<span class="mw-changeslist-separator"></span> ' +

				'<a href="' + mw.util.getUrl(page) + '" title="' + page + '">' + page + '</a> ' +

				'<span class="mw-changeslist-separator"></span> ' +

				(reason ? '<i>(' + reason + ')</i> <span class="mw-changeslist-separator"></span> ' : '') +

				'[ <a href=# class="wping-snooze">snooze</a> | <a href=# class="wping-dismiss">dismiss</a> ]'

			).appendTo($ul);

		}

	});



	if (!pingPages.length) {

		return;

	}



	var $element = $('.mw-rcfilters-ui-changesListWrapperWidget').length ?

		$('.mw-rcfilters-ui-changesListWrapperWidget') :

		( $('.mw-changeslist').length ? // for users of non-AJAX watchlist

			$('.mw-changeslist') :

			$('.mw-changeslist-empty') );



	$element.before(

		$('<div>').attr('id', 'wping').append(

			$('<h4>').text('Pings'),

			$ul

		)

	);



	// check if pinged pages exists, if not turn the links red, occurs lazily

	// XXX: only works if there are <50 pages

	WPing.api.get({ titles: pingPages }).then(function(json) {

		$.each(Object.values(json.query.pages), function(pageid, data) {

			if (data.missing === '') {

				$ul.find('a[href="' + mw.util.getUrl(data.title) + '"]').addClass('new');

			}

		});

	});



	$ul.find('.wping-snooze').click(function(e) {

		e.preventDefault();

		var page = $(e.target).parent().data('page');

		WPing.pingDialog(page);

	});



	$ul.find('.wping-dismiss').click(function(e) {

		e.preventDefault();

		var page = $(e.target).parent().data('page');



		delete optpage];

		WPing.updatePingList(opt);

		WPing.removePingDisplayLine(page);

		mw.track('counter.gadget_WPing.ping_dismissed');

	});



};



WPing.updatePingList = function(opt) {

	var optString = JSON.stringify(opt);



	// update object locally too, so that it can be retrieved in case user wants to change reason/date

	// again (before page is reloaded)

	mw.user.options.set('userjs-wping-list', optString);



	return WPing.api.saveOption('userjs-wping-list', optString);

};



WPing.removePingDisplayLine = function removePingDisplayLine(page) {

	$('#wping ul li[data-page="' + $.escapeSelector(page) + '"]').remove();

	if ($('#wping ul').children().length === 0) {

		$('#wping').remove();

	}

};



WPing.buildSpecialPage = function buildSpecialPage() {

	$('#firstHeading').text('Upcoming watchlist pings');

	document.title = 'Upcoming watchlist pings';

	$('#mw-content-text').empty();



	var opt = JSON.parse(mw.user.options.get('userjs-wping-list'));

	if (!opt) {

		opt = {};

	}

	var timezone = WPing.getUserTimeZone();



	var $ul = $('<ul>');

	$.each(opt, function(page, tr) {

		var time = new Date(tr0 * 60000);



		// render wikilinks in reason text, though all links will appear blue

		var reason = tr1].replace(/\[\[:?(?:([^\|\]]+?)\|)?([^\]\|]+?)\]\]/g, function(_, target, text) {

			if (!target) {

				target = text;

			}

			return '<a href="' + mw.util.getUrl(target) + '" title="' + target + '">' + text + '</a>';

		});

		$ul.append(

			$('<li>').html(

				'<a href="' + mw.util.getUrl(page) + '" title="' + page + '">' + page + '</a>: ' +

				(reason ? '(' + reason + ') ' : '') +

				moment(time).utcOffset(timezone).format('HH:mm, D MMMM YYYY')

			)

		);

	});



	$('#mw-content-text').append(

		$('<p>').text('A ping shall be delivered to your watchlist for the following pages, at the specified time in ' + WPing.getTimeZoneString(timezone) + ' time zone:'),

		$ul

	);



	WPing.api.get({ titles: Object.keys(opt) }).then(function(json) {

		$.each(Object.values(json.query.pages), function(pageid, data) {

			if (data.missing === '') {

				$ul.find('a[href="' + mw.util.getUrl(data.title) + '"]').addClass('new');

			}

		});

	});



};



WPing.getUserTimeZone = function() {

	if (WPing.userTimeZone) { // cache it

		return WPing.userTimeZone;

	}

	switch (window.WPing_timezone || 'preferences') {

		case 'utc':

			WPing.userTimeZone = 0;

			break;

		case 'system':

			WPing.userTimeZone = -new Date().getTimezoneOffset();

			break;

		case 'preferences':

			WPing.userTimeZone = parseInt(mw.user.options.get('timecorrection').split('|')[1]);

			break;

	}

	return WPing.userTimeZone;

};



WPing.getTimeZoneString = function(timecorrection) {

	var negative = false;

	if (timecorrection < 0) {

		timecorrection = -timecorrection;

		negative = true;

	}

	var hourCorrection = parseInt(timecorrection/60);

	hourCorrection = (hourCorrection < 10 ? '0' : '') + hourCorrection.toString();



	var minuteCorrection = timecorrection % 60;

	minuteCorrection = (minuteCorrection < 10 ? '0' : '') + minuteCorrection.toString();



	return 'UTC' + (negative ? '–' : '+') + hourCorrection + minuteCorrection;

};



// SET UP

if (mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist') {

	WPing.attachPings();

} else if (mw.config.get('wgPageName') === 'Special:BlankPage/W-Ping') {

	WPing.buildSpecialPage();

} else {

	var pageName = Morebits.pageNameNorm;

	// for Special:Log views where the form at the top of the page was used:

	if (pageName === 'Special:Log') {

		var user = mw.util.getParamValue('user');

		var type = mw.util.getParamValue('type');

		if (type) {

			pageName += '/' + type;

		}

		if (user) {

			pageName += '/' + Morebits.string.toUpperCaseFirstChar(user);

		}

	} else if (pageName === 'Special:Contributions') {

		var user = mw.util.getParamValue('user');

		if (user) {

			pageName += '/' + Morebits.string.toUpperCaseFirstChar(user);

		}

	}

	if (pageName) {

		var li = mw.util.addPortletLink('p-cactions', '#', 'W-Ping', 'ca-wping', 'Schedule a watchlist ping for this page');

		li.addEventListener('click', function(e) {

			e.preventDefault();

			WPing.pingDialog(pageName);

		});	

	}

}





}).catch(function(err) {

	console.error('[W-Ping]:', err);

});



// </nowiki>

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook