7
*/
8
9
/**
10
- * Get common timezone and adjust dates to use this common timezone.
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
15
- * @param {String} tzCookieName: name of cookie to store timezone
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
*/
18
-function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {
19
- var tzCookie = getCookie(tzCookieName);
20
- var tz = tzCookie ? tzCookie : tzDefault;
20
+function 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
24
- if (tz !== 'utc') {
25
- fixDatetimeTZ(tz, tzClassName);
26
- }
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
*/
37
-function fixDatetimeTZ(tz, tzClassName) {
54
+function fixDatetimeTZ(tz, tzClassName, nochange) {
55
// sanity check, method should be ensured by common-lib.js
56
if (!document.getElementsByClassName) {
57
return;
65
for (var i = 0, len = classesFound.length; i < len; i++) {
66
var curElement = classesFound[i];
67
51
- // we use *.firstChild.data (W3C DOM) instead of *.innerHTML
52
- // as the latter doesn't always work everywhere in every browser
53
- var epoch = parseRFC2822Date(curElement.firstChild.data);
54
- var adjusted = formatDateRFC2882(epoch, tz);
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
+ */
95
+function 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
+ */
135
+function 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
+ */
181
+function 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
56
- curElement.firstChild.data = adjusted;
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
+ */
220
+function 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
+ */
238
+function 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±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
+ */
262
+function 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
+ */
295
+function 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
+ */
320
+function 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 */