Git fork
at reftables-rust 330 lines 11 kB view raw
1// Copyright (C) 2011, John 'Warthog9' Hawley <warthog9@eaglescrag.net> 2// 2011, Jakub Narebski <jnareb@gmail.com> 3 4/** 5 * @fileOverview Manipulate dates in gitweb output, adjusting timezone 6 * @license GPLv2 or later 7 */ 8 9/** 10 * Get common timezone, add UI for changing timezones, and adjust 11 * dates to use requested common timezone. 12 * 13 * This function is called during onload event (added to window.onload). 14 * 15 * @param {String} tzDefault: default timezone, if there is no cookie 16 * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone 17 * @param {String} tzCookieInfo.name: name of cookie to store timezone 18 * @param {String} tzClassName: denotes elements with date to be adjusted 19 */ 20function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) { 21 var tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo); 22 var tz = tzDefault; 23 24 if (tzCookieTZ) { 25 // set timezone to value saved in a cookie 26 tz = tzCookieTZ; 27 // refresh cookie, so its expiration counts from last use of gitweb 28 setCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo); 29 } 30 31 // add UI for changing timezone 32 addChangeTZ(tz, tzCookieInfo, tzClassName); 33 34 // server-side of gitweb produces datetime in UTC, 35 // so if tz is 'utc' there is no need for changes 36 var nochange = tz === 'utc'; 37 38 // adjust dates to use specified common timezone 39 fixDatetimeTZ(tz, tzClassName, nochange); 40} 41 42 43/* ...................................................................... */ 44/* Changing dates to use requested timezone */ 45 46/** 47 * Replace RFC-2822 dates contained in SPAN elements with tzClassName 48 * CSS class with equivalent dates in given timezone. 49 * 50 * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local' 51 * @param {String} tzClassName: specifies elements to be changed 52 * @param {Boolean} nochange: markup for timezone change, but don't change it 53 */ 54function fixDatetimeTZ(tz, tzClassName, nochange) { 55 // sanity check, method should be ensured by common-lib.js 56 if (!document.getElementsByClassName) { 57 return; 58 } 59 60 // translate to timezone in '(-|+)HHMM' format 61 tz = normalizeTimezoneInfo(tz); 62 63 // NOTE: result of getElementsByClassName should probably be cached 64 var classesFound = document.getElementsByClassName(tzClassName, "span"); 65 for (var i = 0, len = classesFound.length; i < len; i++) { 66 var curElement = classesFound[i]; 67 68 curElement.title = 'Click to change timezone'; 69 if (!nochange) { 70 // we use *.firstChild.data (W3C DOM) instead of *.innerHTML 71 // as the latter doesn't always work everywhere in every browser 72 var epoch = parseRFC2822Date(curElement.firstChild.data); 73 var adjusted = formatDateRFC2882(epoch, tz); 74 75 curElement.firstChild.data = adjusted; 76 } 77 } 78} 79 80 81/* ...................................................................... */ 82/* Adding triggers, generating timezone menu, displaying and hiding */ 83 84/** 85 * Adds triggers for UI to change common timezone used for dates in 86 * gitweb output: it marks up and/or creates item to click to invoke 87 * timezone change UI, creates timezone UI fragment to be attached, 88 * and installs appropriate onclick trigger (via event delegation). 89 * 90 * @param {String} tzSelected: pre-selected timezone, 91 * 'utc' or 'local' or '(-|+)HHMM' 92 * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone 93 * @param {String} tzClassName: specifies elements to install trigger 94 */ 95function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) { 96 // make link to timezone UI discoverable 97 addCssRule('.'+tzClassName + ':hover', 98 'text-decoration: underline; cursor: help;'); 99 100 // create form for selecting timezone (to be saved in a cookie) 101 var tzSelectFragment = document.createDocumentFragment(); 102 tzSelectFragment = createChangeTZForm(tzSelectFragment, 103 tzSelected, tzCookieInfo, tzClassName); 104 105 // event delegation handler for timezone selection UI (clicking on entry) 106 // see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ 107 // assumes that there is no existing document.onclick handler 108 document.onclick = function onclickHandler(event) { 109 //IE doesn't pass in the event object 110 event = event || window.event; 111 112 //IE uses srcElement as the target 113 var target = event.target || event.srcElement; 114 115 switch (target.className) { 116 case tzClassName: 117 // don't display timezone menu if it is already displayed 118 if (tzSelectFragment.childNodes.length > 0) { 119 displayChangeTZForm(target, tzSelectFragment); 120 } 121 break; 122 } // end switch 123 }; 124} 125 126/** 127 * Create DocumentFragment with UI for changing common timezone in 128 * which dates are shown in. 129 * 130 * @param {DocumentFragment} documentFragment: where attach UI 131 * @param {String} tzSelected: default (pre-selected) timezone 132 * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone 133 * @returns {DocumentFragment} 134 */ 135function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) { 136 var div = document.createElement("div"); 137 div.className = 'popup'; 138 139 /* '<div class="close-button" title="(click on this box to close)">X</div>' */ 140 var closeButton = document.createElement('div'); 141 closeButton.className = 'close-button'; 142 closeButton.title = '(click on this box to close)'; 143 closeButton.appendChild(document.createTextNode('X')); 144 closeButton.onclick = closeTZFormHandler(documentFragment, tzClassName); 145 div.appendChild(closeButton); 146 147 /* 'Select timezone: <br clear="all">' */ 148 div.appendChild(document.createTextNode('Select timezone: ')); 149 var br = document.createElement('br'); 150 br.clear = 'all'; 151 div.appendChild(br); 152 153 /* '<select name="tzoffset"> 154 * ... 155 * <option value="-0700">UTC-07:00</option> 156 * <option value="-0600">UTC-06:00</option> 157 * ... 158 * </select>' */ 159 var select = document.createElement("select"); 160 select.name = "tzoffset"; 161 //select.style.clear = 'all'; 162 select.appendChild(generateTZOptions(tzSelected)); 163 select.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName); 164 div.appendChild(select); 165 166 documentFragment.appendChild(div); 167 168 return documentFragment; 169} 170 171 172/** 173 * Hide (remove from DOM) timezone change UI, ensuring that it is not 174 * garbage collected and that it can be re-enabled later. 175 * 176 * @param {DocumentFragment} documentFragment: contains detached UI 177 * @param {HTMLSelectElement} target: select element inside of UI 178 * @param {String} tzClassName: specifies element where UI was installed 179 * @returns {DocumentFragment} documentFragment 180 */ 181function removeChangeTZForm(documentFragment, target, tzClassName) { 182 // find containing element, where we appended timezone selection UI 183 // `target' is somewhere inside timezone menu 184 var container = target.parentNode, popup = target; 185 while (container && 186 container.className !== tzClassName) { 187 popup = container; 188 container = container.parentNode; 189 } 190 // safety check if we found correct container, 191 // and if it isn't deleted already 192 if (!container || !popup || 193 container.className !== tzClassName || 194 popup.className !== 'popup') { 195 return documentFragment; 196 } 197 198 // timezone selection UI was appended as last child 199 // see also displayChangeTZForm function 200 var removed = popup.parentNode.removeChild(popup); 201 if (documentFragment.firstChild !== removed) { // the only child 202 // re-append it so it would be available for next time 203 documentFragment.appendChild(removed); 204 } 205 // all of inline style was added by this script 206 // it is not really needed to remove it, but it is a good practice 207 container.removeAttribute('style'); 208 209 return documentFragment; 210} 211 212 213/** 214 * Display UI for changing common timezone for dates in gitweb output. 215 * To be used from 'onclick' event handler. 216 * 217 * @param {HTMLElement} target: where to install/display UI 218 * @param {DocumentFragment} tzSelectFragment: timezone selection UI 219 */ 220function displayChangeTZForm(target, tzSelectFragment) { 221 // for absolute positioning to be related to target element 222 target.style.position = 'relative'; 223 target.style.display = 'inline-block'; 224 225 // show/display UI for changing timezone 226 target.appendChild(tzSelectFragment); 227} 228 229 230/* ...................................................................... */ 231/* List of timezones for timezone selection menu */ 232 233/** 234 * Generate list of timezones for creating timezone select UI 235 * 236 * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' } 237 */ 238function generateTZList() { 239 var timezones = [ 240 { value: "utc", descr: "UTC/GMT"}, 241 { value: "local", descr: "Local (per browser)"} 242 ]; 243 244 // generate all full hour timezones (no fractional timezones) 245 for (var x = -12, idx = timezones.length; x <= +14; x++, idx++) { 246 var hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2); 247 timezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'}; 248 if (x === 0) { 249 timezones[idx].descr = 'UTC\u00B100:00'; // 'UTC&plusmn;00:00' 250 } 251 } 252 253 return timezones; 254} 255 256/** 257 * Generate <options> elements for timezone select UI 258 * 259 * @param {String} tzSelected: default timezone 260 * @returns {DocumentFragment} list of options elements to appendChild 261 */ 262function generateTZOptions(tzSelected) { 263 var elems = document.createDocumentFragment(); 264 var timezones = generateTZList(); 265 266 for (var i = 0, len = timezones.length; i < len; i++) { 267 var tzone = timezones[i]; 268 var option = document.createElement("option"); 269 if (tzone.value === tzSelected) { 270 option.defaultSelected = true; 271 } 272 option.value = tzone.value; 273 option.appendChild(document.createTextNode(tzone.descr)); 274 275 elems.appendChild(option); 276 } 277 278 return elems; 279} 280 281 282/* ...................................................................... */ 283/* Event handlers and/or their generators */ 284 285/** 286 * Create event handler that select timezone and closes timezone select UI. 287 * To be used as $('select[name="tzselect"]').onchange handler. 288 * 289 * @param {DocumentFragment} tzSelectFragment: timezone selection UI 290 * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone 291 * @param {String} tzCookieInfo.name: name of cookie to save result of selection 292 * @param {String} tzClassName: specifies element where UI was installed 293 * @returns {Function} event handler 294 */ 295function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) { 296 //return function selectTZ(event) { 297 return function (event) { 298 event = event || window.event; 299 var target = event.target || event.srcElement; 300 301 var selected = target.options.item(target.selectedIndex); 302 removeChangeTZForm(tzSelectFragment, target, tzClassName); 303 304 if (selected) { 305 selected.defaultSelected = true; 306 setCookie(tzCookieInfo.name, selected.value, tzCookieInfo); 307 fixDatetimeTZ(selected.value, tzClassName); 308 } 309 }; 310} 311 312/** 313 * Create event handler that closes timezone select UI. 314 * To be used e.g. as $('.closebutton').onclick handler. 315 * 316 * @param {DocumentFragment} tzSelectFragment: timezone selection UI 317 * @param {String} tzClassName: specifies element where UI was installed 318 * @returns {Function} event handler 319 */ 320function closeTZFormHandler(tzSelectFragment, tzClassName) { 321 //return function closeTZForm(event) { 322 return function (event) { 323 event = event || window.event; 324 var target = event.target || event.srcElement; 325 326 removeChangeTZForm(tzSelectFragment, target, tzClassName); 327 }; 328} 329 330/* end of adjust-timezone.js */