Jump to content

User:Unready/ui.clock.js

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.
/**
 * Implement a UTC clock and calendar
 * Attach it to the portlet bar
 *
 * Version 1.0: 18 Jul 2014
 *   Original version for Wikia
 *   Purge on click
 *   Use the interval idea from
 *     mw:MediaWiki:Gadget-UTCLiveClock.js
 * Version 2.0: 8 Oct 2016
 *   Rewrite and refactor
 *   Remove purge & add calendar
 *   Support generic MediaWiki skins only
 */
((window.user = window.user || {}).ui = user.ui || {}).clock =
user.ui.clock || (function (mw, $) {
	'use strict';

	var
		self = {
			run : run,
			stop : stop,
			message: new Date().toISOString() + ' Initializing',
			version : 'Version 2.0: 8 Oct 2016'
		},
		gCancel = false,
		gID = -1,   // cannot run = -1; okay to run = 0; already running > 0
		gLast = -1, // current month on the last tick
		gTime = document.createTextNode('');

	// calculate day of week using Zeller's algorithm
	// 0 = Saturday, ..., 6 = Friday
	function zeller(d, m, y) {
		var
			Y = y - (m < 3 ? 1 : 0),
			c = Math.floor(Y / 100),
			w;

		m += (m < 3) ? 12 : 0;
		y = Y % 100;
		w = (d + Math.floor(13 * (m + 1) / 5) + y +
			Math.floor(y / 4) + Math.floor(c / 4) - 2 * c) % 7;
		return (w < 0) ? w + 7 : w;
	}

	// generate an array of weeks for a month table
	// m = month, 0 = January
	// y = 4-digit year
	function month(m, y) {
		var
			month = [
				'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
				'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
			],
			last = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
			cal = [
				'<tr><th colspan="7">' +
					month[m] + ' ' + y +
				'</th></tr>',
				'<tr>' +
					'<th>Su</th><th>Mo</th><th>Tu</th><th>We</th>' +
					'<th>Th</th><th>Fr</th><th>Sa</th>' +
				'</tr>'
			],
			i, offset, tr;

		// leap year
		if (((y % 100 !== 0) && (y % 4 === 0)) || (y % 400 === 0)) {
			last[1] = 29;
		}
		offset = (zeller(1, m + 1, y) + 6) % 7; // 0 = Sunday
		if (offset) {
			tr = '<tr><td colspan="' + offset + '"></td>';
		}
		for ( i = 1; i <= last[m]; ++i ) {
			if (offset === 0) {
				tr = '<tr>';
			}
			tr += '<td>' + i + '</td>';
			if (++offset === 7) {
				tr += '</tr>';
				cal.push(tr);
				offset = 0;
			}
		}
		if (offset) {
			tr += '<td colspan="' + (7 - offset) + '"></td></tr>';
			cal.push(tr);
		}
		return cal;
	}

	// generate 3 months of calendars
	// m = current month, 0 = January
	// y = 4-digit year
	function updateCal(m, y) {
		var
			tbody = $('tbody', '#js-clock-cal').empty();

		if (--m < 0) {
			m = 11;
			--y;
		}
		tbody.append(month(m, y));
		if (++m > 11) {
			m = 0;
			++y;
		}
		tbody.append(month(m, y));
		if (++m > 11) {
			m = 0;
			++y;
		}
		tbody.append(month(m, y));
	}

	// update the displayed time
	function onTick() {
		var
			n = Date.now(),
			d = new Date(n),
			m = d.getUTCMonth(),
			y = d.getUTCFullYear(),
			s = d.toUTCString() // mostly RFC-822/RFC-1123 time string
				.replace(/\d{4}/, '$&,')
				.replace('GMT', '(UTC)')
				.replace(/ 0/g, ' ')
				.substr(5);

		if (!gCancel) {
			gID = setTimeout(onTick, 1100 - n % 1000);
			gTime.data = s;
			if (m !== gLast) {
				gLast = m;
				updateCal(m, y);
			}
		}
	}

	// start the clock, if it's stopped
	function run() {
		if (gID === 0) {
			gCancel = false;
			self.message += '\n' + new Date().toISOString() +
				' Restarted';
			onTick();
		}
	}

	// stop the clock, if it's running
	function stop() {
		if (gID > 0) {
			clearTimeout(gID);
			gID = 0;
			gCancel = true;
			self.message += '\n' + new Date().toISOString() +
				' Stopped';
		}
	}

	// insert nodes into the DOM & run
	$(function () {
		var
			wgSkin = mw.config.get('skin'),
			a = [
				$('<a id="js-clock-time"></a>').append(gTime),
				'<table id="js-clock-cal" class="wikitable"><tbody></tbody></table>'
			];

		// insert the clock into the DOM
		// support generic MediaWiki skins only
		switch (wgSkin) {
		case 'cologneblue':
			$('#syslinks')
				.prepend(document.createTextNode(' | '))
				.prepend(
					$('<span id="pt-clock"></span>')
						.append(a)
				);
			break;
		case 'modern':
			$('#p-personal > .pBody > ul')
				.append(
					$('<li id="pt-clock"></li>')
						.append(a)
				);
			break;
		case 'monobook':
			$('#p-personal > .pBody > ul')
				.prepend(
					$('<li id="pt-clock"></li>')
						.append(a)
				);
			break;
		case 'vector':
			$('#p-personal > ul')
				.prepend(
					$('<li id="pt-clock"></li>')
						.append(a)
				);
			break;
		default:
			self.message += '\n' + new Date().toISOString() +
				' main :: unsupported skin : ' + wgSkin;
			delete self.run;
			delete self.stop;
			return;
		}
		// run it
		onTick();
	});

	return self;
}(mediaWiki, jQuery));