-// 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('button');
- closeButton.className = 'close-button';
- closeButton.title = '(click on this button to close)';
- closeButton.appendChild(document.createTextNode('×'));
- 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 */