diff options
Diffstat (limited to 'gitweb/static/js/adjust-timezone.js')
| -rw-r--r-- | gitweb/static/js/adjust-timezone.js | 330 | 
1 files changed, 330 insertions, 0 deletions
| diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js new file mode 100644 index 0000000000..0c67779500 --- /dev/null +++ b/gitweb/static/js/adjust-timezone.js @@ -0,0 +1,330 @@ +// Copyright (C) 2011, John 'Warthog9' Hawley <warthog9@eaglescrag.net> +//               2011, Jakub Narebski <jnareb@gmail.com> + +/** + * @fileOverview Manipulate dates in gitweb output, adjusting timezone + * @license GPLv2 or later + */ + +/** + * Get common timezone, add UI for changing timezones, and adjust + * dates to use requested common timezone. + * + * This function is called during onload event (added to window.onload). + * + * @param {String} tzDefault: default timezone, if there is no cookie + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @param {String} tzCookieInfo.name: name of cookie to store timezone + * @param {String} tzClassName: denotes elements with date to be adjusted + */ +function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) { +	var tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo); +	var tz = tzDefault; + +	if (tzCookieTZ) { +		// set timezone to value saved in a cookie +		tz = tzCookieTZ; +		// refresh cookie, so its expiration counts from last use of gitweb +		setCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo); +	} + +	// add UI for changing timezone +	addChangeTZ(tz, tzCookieInfo, tzClassName); + +	// server-side of gitweb produces datetime in UTC, +	// so if tz is 'utc' there is no need for changes +	var nochange = tz === 'utc'; + +	// adjust dates to use specified common timezone +	fixDatetimeTZ(tz, tzClassName, nochange); +} + + +/* ...................................................................... */ +/* Changing dates to use requested timezone */ + +/** + * Replace RFC-2822 dates contained in SPAN elements with tzClassName + * CSS class with equivalent dates in given timezone. + * + * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local' + * @param {String} tzClassName: specifies elements to be changed + * @param {Boolean} nochange: markup for timezone change, but don't change it + */ +function fixDatetimeTZ(tz, tzClassName, nochange) { +	// sanity check, method should be ensured by common-lib.js +	if (!document.getElementsByClassName) { +		return; +	} + +	// translate to timezone in '(-|+)HHMM' format +	tz = normalizeTimezoneInfo(tz); + +	// NOTE: result of getElementsByClassName should probably be cached +	var classesFound = document.getElementsByClassName(tzClassName, "span"); +	for (var i = 0, len = classesFound.length; i < len; i++) { +		var curElement = classesFound[i]; + +		curElement.title = 'Click to change timezone'; +		if (!nochange) { +			// we use *.firstChild.data (W3C DOM) instead of *.innerHTML +			// as the latter doesn't always work everywhere in every browser +			var epoch = parseRFC2822Date(curElement.firstChild.data); +			var adjusted = formatDateRFC2882(epoch, tz); + +			curElement.firstChild.data = adjusted; +		} +	} +} + + +/* ...................................................................... */ +/* Adding triggers, generating timezone menu, displaying and hiding */ + +/** + * Adds triggers for UI to change common timezone used for dates in + * gitweb output: it marks up and/or creates item to click to invoke + * timezone change UI, creates timezone UI fragment to be attached, + * and installs appropriate onclick trigger (via event delegation). + * + * @param {String} tzSelected: pre-selected timezone, + *                             'utc' or 'local' or '(-|+)HHMM' + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @param {String} tzClassName: specifies elements to install trigger + */ +function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) { +	// make link to timezone UI discoverable +	addCssRule('.'+tzClassName + ':hover', +	           'text-decoration: underline; cursor: help;'); + +	// create form for selecting timezone (to be saved in a cookie) +	var tzSelectFragment = document.createDocumentFragment(); +	tzSelectFragment = createChangeTZForm(tzSelectFragment, +	                                      tzSelected, tzCookieInfo, tzClassName); + +	// event delegation handler for timezone selection UI (clicking on entry) +	// see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ +	// assumes that there is no existing document.onclick handler +	document.onclick = function onclickHandler(event) { +		//IE doesn't pass in the event object +		event = event || window.event; + +		//IE uses srcElement as the target +		var target = event.target || event.srcElement; + +		switch (target.className) { +		case tzClassName: +			// don't display timezone menu if it is already displayed +			if (tzSelectFragment.childNodes.length > 0) { +				displayChangeTZForm(target, tzSelectFragment); +			} +			break; +		} // end switch +	}; +} + +/** + * Create DocumentFragment with UI for changing common timezone in + * which dates are shown in. + * + * @param {DocumentFragment} documentFragment: where attach UI + * @param {String} tzSelected: default (pre-selected) timezone + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @returns {DocumentFragment} + */ +function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) { +	var div = document.createElement("div"); +	div.className = 'popup'; + +	/* '<div class="close-button" title="(click on this box to close)">X</div>' */ +	var closeButton = document.createElement('div'); +	closeButton.className = 'close-button'; +	closeButton.title = '(click on this box to close)'; +	closeButton.appendChild(document.createTextNode('X')); +	closeButton.onclick = closeTZFormHandler(documentFragment, tzClassName); +	div.appendChild(closeButton); + +	/* 'Select timezone: <br clear="all">' */ +	div.appendChild(document.createTextNode('Select timezone: ')); +	var br = document.createElement('br'); +	br.clear = 'all'; +	div.appendChild(br); + +	/* '<select name="tzoffset"> +	 *    ... +	 *    <option value="-0700">UTC-07:00</option> +	 *    <option value="-0600">UTC-06:00</option> +	 *    ... +	 *  </select>' */ +	var select = document.createElement("select"); +	select.name = "tzoffset"; +	//select.style.clear = 'all'; +	select.appendChild(generateTZOptions(tzSelected)); +	select.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName); +	div.appendChild(select); + +	documentFragment.appendChild(div); + +	return documentFragment; +} + + +/** + * Hide (remove from DOM) timezone change UI, ensuring that it is not + * garbage collected and that it can be re-enabled later. + * + * @param {DocumentFragment} documentFragment: contains detached UI + * @param {HTMLSelectElement} target: select element inside of UI + * @param {String} tzClassName: specifies element where UI was installed + * @returns {DocumentFragment} documentFragment + */ +function removeChangeTZForm(documentFragment, target, tzClassName) { +	// find containing element, where we appended timezone selection UI +	// `target' is somewhere inside timezone menu +	var container = target.parentNode, popup = target; +	while (container && +	       container.className !== tzClassName) { +		popup = container; +		container = container.parentNode; +	} +	// safety check if we found correct container, +	// and if it isn't deleted already +	if (!container || !popup || +	    container.className !== tzClassName || +	    popup.className     !== 'popup') { +		return documentFragment; +	} + +	// timezone selection UI was appended as last child +	// see also displayChangeTZForm function +	var removed = popup.parentNode.removeChild(popup); +	if (documentFragment.firstChild !== removed) { // the only child +		// re-append it so it would be available for next time +		documentFragment.appendChild(removed); +	} +	// all of inline style was added by this script +	// it is not really needed to remove it, but it is a good practice +	container.removeAttribute('style'); + +	return documentFragment; +} + + +/** + * Display UI for changing common timezone for dates in gitweb output. + * To be used from 'onclick' event handler. + * + * @param {HTMLElement} target: where to install/display UI + * @param {DocumentFragment} tzSelectFragment: timezone selection UI + */ +function displayChangeTZForm(target, tzSelectFragment) { +	// for absolute positioning to be related to target element +	target.style.position = 'relative'; +	target.style.display = 'inline-block'; + +	// show/display UI for changing timezone +	target.appendChild(tzSelectFragment); +} + + +/* ...................................................................... */ +/* List of timezones for timezone selection menu */ + +/** + * Generate list of timezones for creating timezone select UI + * + * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' } + */ +function generateTZList() { +	var timezones = [ +		{ value: "utc",   descr: "UTC/GMT"}, +		{ value: "local", descr: "Local (per browser)"} +	]; + +	// generate all full hour timezones (no fractional timezones) +	for (var x = -12, idx = timezones.length; x <= +14; x++, idx++) { +		var hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2); +		timezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'}; +		if (x === 0) { +			timezones[idx].descr = 'UTC\u00B100:00'; // 'UTC±00:00' +		} +	} + +	return timezones; +} + +/** + * Generate <options> elements for timezone select UI + * + * @param {String} tzSelected: default timezone + * @returns {DocumentFragment} list of options elements to appendChild + */ +function generateTZOptions(tzSelected) { +	var elems = document.createDocumentFragment(); +	var timezones = generateTZList(); + +	for (var i = 0, len = timezones.length; i < len; i++) { +		var tzone = timezones[i]; +		var option = document.createElement("option"); +		if (tzone.value === tzSelected) { +			option.defaultSelected = true; +		} +		option.value = tzone.value; +		option.appendChild(document.createTextNode(tzone.descr)); + +		elems.appendChild(option); +	} + +	return elems; +} + + +/* ...................................................................... */ +/* Event handlers and/or their generators */ + +/** + * Create event handler that select timezone and closes timezone select UI. + * To be used as $('select[name="tzselect"]').onchange handler. + * + * @param {DocumentFragment} tzSelectFragment: timezone selection UI + * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone + * @param {String} tzCookieInfo.name: name of cookie to save result of selection + * @param {String} tzClassName: specifies element where UI was installed + * @returns {Function} event handler + */ +function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) { +	//return function selectTZ(event) { +	return function (event) { +		event = event || window.event; +		var target = event.target || event.srcElement; + +		var selected = target.options.item(target.selectedIndex); +		removeChangeTZForm(tzSelectFragment, target, tzClassName); + +		if (selected) { +			selected.defaultSelected = true; +			setCookie(tzCookieInfo.name, selected.value, tzCookieInfo); +			fixDatetimeTZ(selected.value, tzClassName); +		} +	}; +} + +/** + * Create event handler that closes timezone select UI. + * To be used e.g. as $('.closebutton').onclick handler. + * + * @param {DocumentFragment} tzSelectFragment: timezone selection UI + * @param {String} tzClassName: specifies element where UI was installed + * @returns {Function} event handler + */ +function closeTZFormHandler(tzSelectFragment, tzClassName) { +	//return function closeTZForm(event) { +	return function (event) { +		event = event || window.event; +		var target = event.target || event.srcElement; + +		removeChangeTZForm(tzSelectFragment, target, tzClassName); +	}; +} + +/* end of adjust-timezone.js */ | 
