git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 00/11] gitweb: Change timezone
@ 2011-04-09 22:49 Jakub Narebski
  2011-04-09 22:49 ` [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build Jakub Narebski
                   ` (11 more replies)
  0 siblings, 12 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

This is split version (with assorted cleanups) of J.H. patch adding
JavaScript-base ability to change timezone in which dates are
displayed.

  [PATCH 0/1] Gitweb: Change timezone
    [PATCH 1/1] gitweb: javascript ability to adjust time based on timezone
  Message-Id: <1300925335-3212-1-git-send-email-warthog9@eaglescrag.net>
  http://thread.gmane.org/gmane.comp.version-control.git/169384/focus=169881

Below there is copy of original J.H. announcement:
JH>
JH> This is just a javascript implementation of Kevin's localtime
JH> feature.  It's pretty straight forward, though date handling in
JH> Javascript is, special (head bangingly so).
JH> 
JH> This should be good to run on any browser, with the safe fallback
JH> of UTC being the default output should Javascript not work / be
JH> available.


Table of contents:
~~~~~~~~~~~~~~~~~~
* [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build

  Addresses issue with 'add several javascript files to be loaded'

* [PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files
  [PATCH/RFC 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft

  Both are post-split cleanup

* [PATCH 04/11] gitweb.js: Extract and improve datetime handling
  [PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js
  [PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists)

  Creating JavaScript library

* [PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html
  [RFC/PATCH 08/11] gitweb: Unify the way long timestamp is displayed

  Preparing gitweb.perl for changes

* [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone
  [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates
  [PATCH/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable

  The main part.


Shortlog:
~~~~~~~~~
Jakub Narebski (9):
  gitweb: Split JavaScript for maintability, combining on build
  gitweb.js: Update and improve comments in JavaScript files
  gitweb.js: Provide default values for padding in padLeftStr and padLeft
  gitweb.js: Extract and improve datetime handling
  gitweb.js: Introduce gitweb/static/js/lib/cookies.js
  gitweb.js: Provide getElementsByClassName method (if it not exists)
  gitweb: Refactor generating of long dates into format_timestamp_html
  gitweb: Unify the way long timestamp is displayed
  gitweb: Make JavaScript ability to adjust timezones configurable

John 'Warthog9' Hawley (2):
  gitweb: JavaScript ability to adjust time based on timezone
  gitweb.js: Add UI for selecting common timezone to display dates

Diffstat:
~~~~~~~~~
 .gitignore                                         |    1 +
 gitweb/Makefile                                    |   19 ++-
 gitweb/gitweb.perl                                 |   75 ++++--
 gitweb/static/gitweb.css                           |   25 ++
 gitweb/static/js/README                            |   20 ++
 gitweb/static/js/adjust-timezone.js                |  281 ++++++++++++++++++++
 .../static/{gitweb.js => js/blame_incremental.js}  |  228 +---------------
 gitweb/static/js/javascript-detection.js           |   43 +++
 gitweb/static/js/lib/common-lib.js                 |  224 ++++++++++++++++
 gitweb/static/js/lib/cookies.js                    |  114 ++++++++
 gitweb/static/js/lib/datetime.js                   |  176 ++++++++++++
 11 files changed, 965 insertions(+), 241 deletions(-)
 create mode 100644 gitweb/static/js/README
 create mode 100644 gitweb/static/js/adjust-timezone.js
 rename gitweb/static/{gitweb.js => js/blame_incremental.js} (74%)
 create mode 100644 gitweb/static/js/javascript-detection.js
 create mode 100644 gitweb/static/js/lib/common-lib.js
 create mode 100644 gitweb/static/js/lib/cookies.js
 create mode 100644 gitweb/static/js/lib/datetime.js

Dirstat:
~~~~~~~~
  42.5% gitweb/static/js/lib/
  29.5% gitweb/static/js/
  18.9% gitweb/static/
   8.9% gitweb/

-- 
1.7.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files Jakub Narebski
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

Split originally single gitweb.js file into smaller files, each
dealing with single issue / area of responsibility.  This move should
make gitweb's JavaScript code easier to maintain.

For better webapp performance it is recommended[1][2][3] to combine
JavaScript files.  Do it during build time (in gitweb/Makefile), by
straight concatenation of files into gitweb.js file (which is now
ignored as being generated).  This means that there are no changes to
gitweb script itself - it still uses gitweb.js or gitweb.min.js, but
now generated.

[1]: http://developer.yahoo.com/performance/rules.html
     "Minimize HTTP Requests" section
[2]: http://code.google.com/speed/articles/include-scripts-properly.html
     "1. Combine external JavaScript files"
[3]: http://javascript-reference.info/speed-up-your-javascript-load-time.htm
     "Combine Your Files" section.

See also new gitweb/static/js/README file.

Inspired-by-patch-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Original J.H. patch had
JH>
JH> Enabling this will then add several javascript files to be loaded,
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

While I agree with J.H. that for maintenance reasons JavaScript files
should be split, most web best practice pages recommends to combine
JavaScript files for performance.  This patch allows both.


What is unfortunately not visible from diffstat below is that it is
almost straightforward split of original gitweb/statis/gitweb.js file,
with only some header and footer comments added.

Is there any magic git-diff / git-show onvocation that is able to turn
split into patch involving solely renames / copies in split result?

 .gitignore                                         |    1 +
 gitweb/Makefile                                    |   16 ++-
 gitweb/static/js/README                            |   20 ++
 .../static/{gitweb.js => js/blame_incremental.js}  |  208 +-------------------
 gitweb/static/js/javascript-detection.js           |   43 ++++
 gitweb/static/js/lib/common-lib.js                 |  187 ++++++++++++++++++
 6 files changed, 269 insertions(+), 206 deletions(-)
 create mode 100644 gitweb/static/js/README
 rename gitweb/static/{gitweb.js => js/blame_incremental.js} (76%)
 create mode 100644 gitweb/static/js/javascript-detection.js
 create mode 100644 gitweb/static/js/lib/common-lib.js

diff --git a/.gitignore b/.gitignore
index 2cf3ca5..9e95005 100644
--- a/.gitignore
+++ b/.gitignore
@@ -158,6 +158,7 @@
 /gitk-git/gitk-wish
 /gitweb/GITWEB-BUILD-OPTIONS
 /gitweb/gitweb.cgi
+/gitweb/static/gitweb.js
 /gitweb/static/gitweb.min.*
 /test-chmtime
 /test-ctype
diff --git a/gitweb/Makefile b/gitweb/Makefile
index 0a6ac00..0baa9df 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -86,7 +86,7 @@ ifndef V
 endif
 endif
 
-all:: gitweb.cgi
+all:: gitweb.cgi static/gitweb.js
 
 GITWEB_PROGRAMS = gitweb.cgi
 
@@ -112,6 +112,15 @@ endif
 
 GITWEB_FILES += static/git-logo.png static/git-favicon.png
 
+# JavaScript files that are composed (concatenated) to form gitweb.js
+#
+# js/lib/common-lib.js should be always first, then js/lib/*.js,
+# then the rest of files; js/gitweb.js should be last (if it exists)
+GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
+GITWEB_JSLIB_FILES += static/js/javascript-detection.js
+GITWEB_JSLIB_FILES += static/js/blame_incremental.js
+
+
 GITWEB_REPLACE = \
 	-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
 	-e 's|++GIT_BINDIR++|$(bindir)|g' \
@@ -146,6 +155,11 @@ gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
 	chmod +x $@+ && \
 	mv $@+ $@
 
+static/gitweb.js: $(GITWEB_JSLIB_FILES)
+	$(QUIET_GEN)$(RM) $@ $@+ && \
+	cat $^ >$@+ && \
+	mv $@+ $@
+
 ### Testing rules
 
 test:
diff --git a/gitweb/static/js/README b/gitweb/static/js/README
new file mode 100644
index 0000000..f8460ed
--- /dev/null
+++ b/gitweb/static/js/README
@@ -0,0 +1,20 @@
+GIT web interface (gitweb) - JavaScript
+=======================================
+
+This directory holds JavaScript code used by gitweb (GIT web interface).
+Scripts from there would be concatenated together in the order specified
+by gitweb/Makefile into gitweb/static/gitweb.js, during building of
+gitweb/gitweb.cgi (during gitweb building).  The resulting file (or its
+minification) would then be installed / deployed together with gitweb.
+
+Scripts in 'lib/' subdirectory compose generic JavaScript library,
+providing features required by gitweb but in no way limited to gitweb
+only.  In the future those scripts could be replaced by some JavaScript
+library / framework, like e.g. jQuery, YUI, Prototype, MooTools, Dojo,
+ExtJS, Script.aculo.us or SproutCore.
+
+All scripts that manipulate gitweb output should be put outside 'lib/',
+directly in this directory ('gitweb/static/js/').  Those scripts would
+have to be rewritten if gitweb moves to using some JavaScript library.
+
+See also comments in gitweb/Makefile.
diff --git a/gitweb/static/gitweb.js b/gitweb/static/js/blame_incremental.js
similarity index 76%
rename from gitweb/static/gitweb.js
rename to gitweb/static/js/blame_incremental.js
index 40ec084..f63f78b 100644
--- a/gitweb/static/gitweb.js
+++ b/gitweb/static/js/blame_incremental.js
@@ -1,44 +1,12 @@
 // Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
 //               2007, Petr Baudis <pasky@suse.cz>
-//          2008-2009, Jakub Narebski <jnareb@gmail.com>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
 
 /**
- * @fileOverview JavaScript code for gitweb (git web interface).
+ * @fileOverview JavaScript side of Ajax-y 'blame_incremental' view in gitweb
  * @license GPLv2 or later
  */
 
-/* ============================================================ */
-/* functions for generic gitweb actions and views */
-
-/**
- * used to check if link has 'js' query parameter already (at end),
- * and other reasons to not add 'js=1' param at the end of link
- * @constant
- */
-var jsExceptionsRe = /[;?]js=[01]$/;
-
-/**
- * Add '?js=1' or ';js=1' to the end of every link in the document
- * that doesn't have 'js' query parameter set already.
- *
- * Links with 'js=1' lead to JavaScript version of given action, if it
- * exists (currently there is only 'blame_incremental' for 'blame')
- *
- * @globals jsExceptionsRe
- */
-function fixLinks() {
-	var allLinks = document.getElementsByTagName("a") || document.links;
-	for (var i = 0, len = allLinks.length; i < len; i++) {
-		var link = allLinks[i];
-		if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;
-			link.href +=
-				(link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';
-		}
-	}
-}
-
-
-/* ============================================================ */
 
 /*
  * This code uses DOM methods instead of (nonstandard) innerHTML
@@ -59,71 +27,6 @@ function fixLinks() {
 
 
 /* ============================================================ */
-/* generic utility functions */
-
-
-/**
- * pad number N with nonbreakable spaces on the left, to WIDTH characters
- * example: padLeftStr(12, 3, '\u00A0') == '\u00A012'
- *          ('\u00A0' is nonbreakable space)
- *
- * @param {Number|String} input: number to pad
- * @param {Number} width: visible width of output
- * @param {String} str: string to prefix to string, e.g. '\u00A0'
- * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR
- */
-function padLeftStr(input, width, str) {
-	var prefix = '';
-
-	width -= input.toString().length;
-	while (width > 0) {
-		prefix += str;
-		width--;
-	}
-	return prefix + input;
-}
-
-/**
- * Pad INPUT on the left to SIZE width, using given padding character CH,
- * for example padLeft('a', 3, '_') is '__a'.
- *
- * @param {String} input: input value converted to string.
- * @param {Number} width: desired length of output.
- * @param {String} ch: single character to prefix to string.
- *
- * @returns {String} Modified string, at least SIZE length.
- */
-function padLeft(input, width, ch) {
-	var s = input + "";
-	while (s.length < width) {
-		s = ch + s;
-	}
-	return s;
-}
-
-/**
- * Create XMLHttpRequest object in cross-browser way
- * @returns XMLHttpRequest object, or null
- */
-function createRequestObject() {
-	try {
-		return new XMLHttpRequest();
-	} catch (e) {}
-	try {
-		return window.createRequest();
-	} catch (e) {}
-	try {
-		return new ActiveXObject("Msxml2.XMLHTTP");
-	} catch (e) {}
-	try {
-		return new ActiveXObject("Microsoft.XMLHTTP");
-	} catch (e) {}
-
-	return null;
-}
-
-
-/* ============================================================ */
 /* utility/helper functions (and variables) */
 
 var xhr;        // XMLHttpRequest object
@@ -392,111 +295,6 @@ function fixColorsAndGroups() {
 	}
 }
 
-/* ............................................................ */
-/* time and data */
-
-/**
- * used to extract hours and minutes from timezone info, e.g '-0900'
- * @constant
- */
-var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;
-
-/**
- * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
- *
- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
- * @returns {Number} offset from UTC in seconds for timezone
- *
- * @globals tzRe
- */
-function timezoneOffset(timezoneInfo) {
-	var match = tzRe.exec(timezoneInfo);
-	var tz_sign = (match[1] === '-' ? -1 : +1);
-	var tz_hour = parseInt(match[2],10);
-	var tz_min  = parseInt(match[3],10);
-
-	return tz_sign*(((tz_hour*60) + tz_min)*60);
-}
-
-/**
- * return date in local time formatted in iso-8601 like format
- * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
- *
- * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
- * @returns {String} date in local time in iso-8601 like format
- */
-function formatDateISOLocal(epoch, timezoneInfo) {
-	// date corrected by timezone
-	var localDate = new Date(1000 * (epoch +
-		timezoneOffset(timezoneInfo)));
-	var localDateStr = // e.g. '2005-08-07'
-		localDate.getUTCFullYear()                 + '-' +
-		padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
-		padLeft(localDate.getUTCDate(),    2, '0');
-	var localTimeStr = // e.g. '21:49:46'
-		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
-		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
-		padLeft(localDate.getUTCSeconds(), 2, '0');
-
-	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
-}
-
-/* ............................................................ */
-/* unquoting/unescaping filenames */
-
-/**#@+
- * @constant
- */
-var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g;
-var octEscRe = /^[0-7]{1,3}$/;
-var maybeQuotedRe = /^\"(.*)\"$/;
-/**#@-*/
-
-/**
- * unquote maybe git-quoted filename
- * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a	a'
- *
- * @param {String} str: git-quoted string
- * @returns {String} Unquoted and unescaped string
- *
- * @globals escCodeRe, octEscRe, maybeQuotedRe
- */
-function unquote(str) {
-	function unq(seq) {
-		var es = {
-			// character escape codes, aka escape sequences (from C)
-			// replacements are to some extent JavaScript specific
-			t: "\t",   // tab            (HT, TAB)
-			n: "\n",   // newline        (NL)
-			r: "\r",   // return         (CR)
-			f: "\f",   // form feed      (FF)
-			b: "\b",   // backspace      (BS)
-			a: "\x07", // alarm (bell)   (BEL)
-			e: "\x1B", // escape         (ESC)
-			v: "\v"    // vertical tab   (VT)
-		};
-
-		if (seq.search(octEscRe) !== -1) {
-			// octal char sequence
-			return String.fromCharCode(parseInt(seq, 8));
-		} else if (seq in es) {
-			// C escape sequence, aka character escape code
-			return es[seq];
-		}
-		// quoted ordinary character
-		return seq;
-	}
-
-	var match = str.match(maybeQuotedRe);
-	if (match) {
-		str = match[1];
-		// perhaps str = eval('"'+str+'"'); would be enough?
-		str = str.replace(escCodeRe,
-			function (substr, p1, offset, s) { return unq(p1); });
-	}
-	return str;
-}
 
 /* ============================================================ */
 /* main part: parsing response */
@@ -886,4 +684,4 @@ function startBlame(blamedataUrl, bUrl) {
 	pollTimer = setInterval(xhr.onreadystatechange, 1000);
 }
 
-// end of gitweb.js
+/* end of blame_incremental.js */
diff --git a/gitweb/static/js/javascript-detection.js b/gitweb/static/js/javascript-detection.js
new file mode 100644
index 0000000..93dd2bd
--- /dev/null
+++ b/gitweb/static/js/javascript-detection.js
@@ -0,0 +1,43 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Detect if JavaScript is enabled, and pass it to server-side
+ * @license GPLv2 or later
+ */
+
+
+/* ============================================================ */
+/* Manipulating links */
+
+/**
+ * used to check if link has 'js' query parameter already (at end),
+ * and other reasons to not add 'js=1' param at the end of link
+ * @constant
+ */
+var jsExceptionsRe = /[;?]js=[01]$/;
+
+/**
+ * Add '?js=1' or ';js=1' to the end of every link in the document
+ * that doesn't have 'js' query parameter set already.
+ *
+ * Links with 'js=1' lead to JavaScript version of given action, if it
+ * exists (currently there is only 'blame_incremental' for 'blame')
+ *
+ * To be used as `window.onload` handler
+ *
+ * @globals jsExceptionsRe
+ */
+function fixLinks() {
+	var allLinks = document.getElementsByTagName("a") || document.links;
+	for (var i = 0, len = allLinks.length; i < len; i++) {
+		var link = allLinks[i];
+		if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;
+			link.href +=
+				(link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';
+		}
+	}
+}
+
+/* end of javascript-detection.js */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
new file mode 100644
index 0000000..38f3b9e
--- /dev/null
+++ b/gitweb/static/js/lib/common-lib.js
@@ -0,0 +1,187 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Generic JavaScript code (helper functions)
+ * @license GPLv2 or later
+ */
+
+
+/* ============================================================ */
+/* ............................................................ */
+/* Padding */
+
+/**
+ * pad number N with nonbreakable spaces on the left, to WIDTH characters
+ * example: padLeftStr(12, 3, '\u00A0') == '\u00A012'
+ *          ('\u00A0' is nonbreakable space)
+ *
+ * @param {Number|String} input: number to pad
+ * @param {Number} width: visible width of output
+ * @param {String} str: string to prefix to string, e.g. '\u00A0'
+ * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR
+ */
+function padLeftStr(input, width, str) {
+	var prefix = '';
+
+	width -= input.toString().length;
+	while (width > 0) {
+		prefix += str;
+		width--;
+	}
+	return prefix + input;
+}
+
+/**
+ * Pad INPUT on the left to SIZE width, using given padding character CH,
+ * for example padLeft('a', 3, '_') is '__a'.
+ *
+ * @param {String} input: input value converted to string.
+ * @param {Number} width: desired length of output.
+ * @param {String} ch: single character to prefix to string.
+ *
+ * @returns {String} Modified string, at least SIZE length.
+ */
+function padLeft(input, width, ch) {
+	var s = input + "";
+	while (s.length < width) {
+		s = ch + s;
+	}
+	return s;
+}
+
+
+/* ............................................................ */
+/* Ajax */
+
+/**
+ * Create XMLHttpRequest object in cross-browser way
+ * @returns XMLHttpRequest object, or null
+ */
+function createRequestObject() {
+	try {
+		return new XMLHttpRequest();
+	} catch (e) {}
+	try {
+		return window.createRequest();
+	} catch (e) {}
+	try {
+		return new ActiveXObject("Msxml2.XMLHTTP");
+	} catch (e) {}
+	try {
+		return new ActiveXObject("Microsoft.XMLHTTP");
+	} catch (e) {}
+
+	return null;
+}
+
+
+/* ............................................................ */
+/* time and data */
+
+/**
+ * used to extract hours and minutes from timezone info, e.g '-0900'
+ * @constant
+ */
+var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;
+
+/**
+ * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
+ *
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {Number} offset from UTC in seconds for timezone
+ *
+ * @globals tzRe
+ */
+function timezoneOffset(timezoneInfo) {
+	var match = tzRe.exec(timezoneInfo);
+	var tz_sign = (match[1] === '-' ? -1 : +1);
+	var tz_hour = parseInt(match[2],10);
+	var tz_min  = parseInt(match[3],10);
+
+	return tz_sign*(((tz_hour*60) + tz_min)*60);
+}
+
+/**
+ * return date in local time formatted in iso-8601 like format
+ * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {String} date in local time in iso-8601 like format
+ */
+function formatDateISOLocal(epoch, timezoneInfo) {
+	// date corrected by timezone
+	var localDate = new Date(1000 * (epoch +
+		timezoneOffset(timezoneInfo)));
+	var localDateStr = // e.g. '2005-08-07'
+		localDate.getUTCFullYear()                 + '-' +
+		padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
+		padLeft(localDate.getUTCDate(),    2, '0');
+	var localTimeStr = // e.g. '21:49:46'
+		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+		padLeft(localDate.getUTCSeconds(), 2, '0');
+
+	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+
+/* ............................................................ */
+/* unquoting/unescaping filenames */
+
+/**#@+
+ * @constant
+ */
+var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g;
+var octEscRe = /^[0-7]{1,3}$/;
+var maybeQuotedRe = /^\"(.*)\"$/;
+/**#@-*/
+
+/**
+ * unquote maybe git-quoted filename
+ * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a	a'
+ *
+ * @param {String} str: git-quoted string
+ * @returns {String} Unquoted and unescaped string
+ *
+ * @globals escCodeRe, octEscRe, maybeQuotedRe
+ */
+function unquote(str) {
+	function unq(seq) {
+		var es = {
+			// character escape codes, aka escape sequences (from C)
+			// replacements are to some extent JavaScript specific
+			t: "\t",   // tab            (HT, TAB)
+			n: "\n",   // newline        (NL)
+			r: "\r",   // return         (CR)
+			f: "\f",   // form feed      (FF)
+			b: "\b",   // backspace      (BS)
+			a: "\x07", // alarm (bell)   (BEL)
+			e: "\x1B", // escape         (ESC)
+			v: "\v"    // vertical tab   (VT)
+		};
+
+		if (seq.search(octEscRe) !== -1) {
+			// octal char sequence
+			return String.fromCharCode(parseInt(seq, 8));
+		} else if (seq in es) {
+			// C escape sequence, aka character escape code
+			return es[seq];
+		}
+		// quoted ordinary character
+		return seq;
+	}
+
+	var match = str.match(maybeQuotedRe);
+	if (match) {
+		str = match[1];
+		// perhaps str = eval('"'+str+'"'); would be enough?
+		str = str.replace(escCodeRe,
+			function (substr, p1, offset, s) { return unq(p1); });
+	}
+	return str;
+}
+
+/* end of common-lib.js */
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
  2011-04-09 22:49 ` [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH/RFC 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft Jakub Narebski
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

This consists of adding a few extra explanation, fixing descriptions
of functions to match names of parameters in code, adding a few
separators, and fixing spelling -- while at it spell 'neighbor' using
American spelling (and not as 'neighbour').

This is post-split cleanup.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
 gitweb/static/js/blame_incremental.js |   22 +++++++++++-----------
 gitweb/static/js/lib/common-lib.js    |   12 ++++++++----
 2 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js
index f63f78b..676da6b 100644
--- a/gitweb/static/js/blame_incremental.js
+++ b/gitweb/static/js/blame_incremental.js
@@ -7,7 +7,7 @@
  * @license GPLv2 or later
  */
 
-
+/* ============================================================ */
 /*
  * This code uses DOM methods instead of (nonstandard) innerHTML
  * to modify page.
@@ -26,7 +26,7 @@
  */
 
 
-/* ============================================================ */
+/* ............................................................ */
 /* utility/helper functions (and variables) */
 
 var xhr;        // XMLHttpRequest object
@@ -132,7 +132,7 @@ function writeTimeInterval() {
 }
 
 /**
- * show an error message alert to user within page (in prohress info area)
+ * show an error message alert to user within page (in progress info area)
  * @param {String} str: plain text error message (no HTML)
  *
  * @globals div_progress_info
@@ -182,7 +182,7 @@ function getColorNo(tr) {
 
 var colorsFreq = [0, 0, 0];
 /**
- * return one of given possible colors (curently least used one)
+ * return one of given possible colors (currently least used one)
  * example: chooseColorNoFrom(2, 3) returns 2 or 3
  *
  * @param {Number[]} arguments: one or more numbers
@@ -203,8 +203,8 @@ function chooseColorNoFrom() {
 }
 
 /**
- * given two neigbour <tr> elements, find color which would be different
- * from color of both of neighbours; used to 3-color blame table
+ * given two neighbor <tr> elements, find color which would be different
+ * from color of both of neighbors; used to 3-color blame table
  *
  * @param {HTMLElement} tr_prev
  * @param {HTMLElement} tr_next
@@ -216,14 +216,14 @@ function findColorNo(tr_prev, tr_next) {
 	var color_next = getColorNo(tr_next);
 
 
-	// neither of neighbours has color set
+	// neither of neighbors has color set
 	// THEN we can use any of 3 possible colors
 	if (!color_prev && !color_next) {
 		return chooseColorNoFrom(1,2,3);
 	}
 
-	// either both neighbours have the same color,
-	// or only one of neighbours have color set
+	// either both neighbors have the same color,
+	// or only one of neighbors have color set
 	// THEN we can use any color except given
 	var color;
 	if (color_prev === color_next) {
@@ -237,7 +237,7 @@ function findColorNo(tr_prev, tr_next) {
 		return chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1);
 	}
 
-	// neighbours have different colors
+	// neighbors have different colors
 	// THEN there is only one color left
 	return (3 - ((color_prev + color_next) % 3));
 }
@@ -258,7 +258,7 @@ function isStartOfGroup(tr) {
 
 /**
  * change colors to use zebra coloring (2 colors) instead of 3 colors
- * concatenate neighbour commit groups belonging to the same commit
+ * concatenate neighbor commit groups belonging to the same commit
  *
  * @globals colorRe
  */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index 38f3b9e..6a6d200 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -13,14 +13,17 @@
 /* Padding */
 
 /**
- * pad number N with nonbreakable spaces on the left, to WIDTH characters
+ * pad INPUT on the left with STR that is assumed to have visible
+ * width of single character (for example nonbreakable spaces),
+ * to WIDTH characters
+ *
  * example: padLeftStr(12, 3, '\u00A0') == '\u00A012'
  *          ('\u00A0' is nonbreakable space)
  *
  * @param {Number|String} input: number to pad
  * @param {Number} width: visible width of output
  * @param {String} str: string to prefix to string, e.g. '\u00A0'
- * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR
+ * @returns {String} INPUT prefixed with STR x (WIDTH - INPUT.length)
  */
 function padLeftStr(input, width, str) {
 	var prefix = '';
@@ -34,7 +37,7 @@ function padLeftStr(input, width, str) {
 }
 
 /**
- * Pad INPUT on the left to SIZE width, using given padding character CH,
+ * Pad INPUT on the left to WIDTH, using given padding character CH,
  * for example padLeft('a', 3, '_') is '__a'.
  *
  * @param {String} input: input value converted to string.
@@ -140,7 +143,8 @@ var maybeQuotedRe = /^\"(.*)\"$/;
 /**#@-*/
 
 /**
- * unquote maybe git-quoted filename
+ * unquote maybe C-quoted filename (as used by git, i.e. it is
+ * in double quotes '"' if there is any escape character used)
  * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a	a'
  *
  * @param {String} str: git-quoted string
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH/RFC 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
  2011-04-09 22:49 ` [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build Jakub Narebski
  2011-04-09 22:49 ` [PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH 04/11] gitweb.js: Extract and improve datetime handling Jakub Narebski
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

This means that one can use padLeft(4, 2) and it would be equivalent
to runing padLeft(4, 2, '0'), and it would return '04' i.e. '4' padded
with '0' to width 2, to be used e.g. in formatting date and time.

This should make those functions easier to use.  Current code doesn't
yet make use of this feature.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This patch is not strictly necessary, but it makes those functions
easier to use, I guess.

Removing this patch would require some trivially resolvable changes
to next commits in this series.

 gitweb/static/js/lib/common-lib.js |   14 +++++++++++---
 1 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index 6a6d200..c45454e 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -22,11 +22,14 @@
  *
  * @param {Number|String} input: number to pad
  * @param {Number} width: visible width of output
- * @param {String} str: string to prefix to string, e.g. '\u00A0'
+ * @param {String} str: string to prefix to string, defaults to '\u00A0'
  * @returns {String} INPUT prefixed with STR x (WIDTH - INPUT.length)
  */
 function padLeftStr(input, width, str) {
 	var prefix = '';
+	if (typeof str === 'undefined') {
+		ch = '\u00A0'; // using '&nbsp;' doesn't work in all browsers
+	}
 
 	width -= input.toString().length;
 	while (width > 0) {
@@ -38,16 +41,21 @@ function padLeftStr(input, width, str) {
 
 /**
  * Pad INPUT on the left to WIDTH, using given padding character CH,
- * for example padLeft('a', 3, '_') is '__a'.
+ * for example padLeft('a', 3, '_') is '__a'
+ *             padLeft(4, 2) is '04' (same as padLeft(4, 2, '0'))
  *
  * @param {String} input: input value converted to string.
  * @param {Number} width: desired length of output.
- * @param {String} ch: single character to prefix to string.
+ * @param {String} ch: single character to prefix to string, defaults to '0'.
  *
  * @returns {String} Modified string, at least SIZE length.
  */
 function padLeft(input, width, ch) {
 	var s = input + "";
+	if (typeof ch === 'undefined') {
+		ch = '0';
+	}
+
 	while (s.length < width) {
 		s = ch + s;
 	}
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 04/11] gitweb.js: Extract and improve datetime handling
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (2 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH/RFC 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js Jakub Narebski
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

Move formatDateISOLocal(epoch, timezone) function (and also helper
timezoneOffset(timezoneInfo) function it requires) from common-lib.js to
datetime.js

Add new functions:
* localTimezoneOffset - to get browser timezone offset in seconds
* localTimezoneInfo   - to get browser timezone in '(+|-)HHMM' format
* formatTimezoneInfo - turn offset in hours and minutes into '(+|-)HHMM'
* parseRFC2822Date - to parse RFC-2822 dates that gitweb uses into epoch
* formatDateRFC2882 - like formatDateISOLocal, only RFC-2822 format

All those functions are meant to be used in future commit
'gitweb: javascript ability to adjust time based on timezone'

An alternative would be to use e.g. Datejs (http://www.datejs.com)
library.


While at it escape '-' in character class inside tzRe regexp, as
recommended by JSLint (http://www.jslint.com).

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
The major difference to approach taken in J.H. patch is noticing that
while in older browsers Date constructor (Date.parse class method)
does not like ISO-8601 format (W3CDTF variant used in microformats),
all browsers should be able to parse RFC-822 / RFC-2822 date... which
is what gitweb generates.

So instead of using deprecated (because of accessibility
considerations) datetime-design-pattern microformat, or newer
value-class-pattern (perhaps in value-title variant), simply parse
RFC-2822 date that gitweb generated.

 gitweb/Makefile                    |    1 +
 gitweb/static/js/lib/common-lib.js |   51 -----------
 gitweb/static/js/lib/datetime.js   |  161 ++++++++++++++++++++++++++++++++++++
 3 files changed, 162 insertions(+), 51 deletions(-)
 create mode 100644 gitweb/static/js/lib/datetime.js

diff --git a/gitweb/Makefile b/gitweb/Makefile
index 0baa9df..403265a 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -117,6 +117,7 @@ GITWEB_FILES += static/git-logo.png static/git-favicon.png
 # js/lib/common-lib.js should be always first, then js/lib/*.js,
 # then the rest of files; js/gitweb.js should be last (if it exists)
 GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
+GITWEB_JSLIB_FILES += static/js/lib/datetime.js
 GITWEB_JSLIB_FILES += static/js/javascript-detection.js
 GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index c45454e..d6b0c0d 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -89,57 +89,6 @@ function createRequestObject() {
 
 
 /* ............................................................ */
-/* time and data */
-
-/**
- * used to extract hours and minutes from timezone info, e.g '-0900'
- * @constant
- */
-var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;
-
-/**
- * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
- *
- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
- * @returns {Number} offset from UTC in seconds for timezone
- *
- * @globals tzRe
- */
-function timezoneOffset(timezoneInfo) {
-	var match = tzRe.exec(timezoneInfo);
-	var tz_sign = (match[1] === '-' ? -1 : +1);
-	var tz_hour = parseInt(match[2],10);
-	var tz_min  = parseInt(match[3],10);
-
-	return tz_sign*(((tz_hour*60) + tz_min)*60);
-}
-
-/**
- * return date in local time formatted in iso-8601 like format
- * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
- *
- * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
- * @returns {String} date in local time in iso-8601 like format
- */
-function formatDateISOLocal(epoch, timezoneInfo) {
-	// date corrected by timezone
-	var localDate = new Date(1000 * (epoch +
-		timezoneOffset(timezoneInfo)));
-	var localDateStr = // e.g. '2005-08-07'
-		localDate.getUTCFullYear()                 + '-' +
-		padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
-		padLeft(localDate.getUTCDate(),    2, '0');
-	var localTimeStr = // e.g. '21:49:46'
-		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
-		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
-		padLeft(localDate.getUTCSeconds(), 2, '0');
-
-	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
-}
-
-
-/* ............................................................ */
 /* unquoting/unescaping filenames */
 
 /**#@+
diff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js
new file mode 100644
index 0000000..b3dcedb
--- /dev/null
+++ b/gitweb/static/js/lib/datetime.js
@@ -0,0 +1,161 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+//               2007, Petr Baudis <pasky@suse.cz>
+//          2008-2011, Jakub Narebski <jnareb@gmail.com>
+
+/**
+ * @fileOverview Datetime manipulation: parsing and formatting
+ * @license GPLv2 or later
+ */
+
+
+/* ............................................................ */
+/* parsing and retrieving datetime related information */
+
+/**
+ * used to extract hours and minutes from timezone info, e.g '-0900'
+ * @constant
+ */
+var tzRe = /^([+\-])([0-9][0-9])([0-9][0-9])$/;
+
+/**
+ * convert numeric timezone +/-ZZZZ to offset from UTC in seconds
+ *
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {Number} offset from UTC in seconds for timezone
+ *
+ * @globals tzRe
+ */
+function timezoneOffset(timezoneInfo) {
+	var match = tzRe.exec(timezoneInfo);
+	var tz_sign = (match[1] === '-' ? -1 : +1);
+	var tz_hour = parseInt(match[2],10);
+	var tz_min  = parseInt(match[3],10);
+
+	return tz_sign*(((tz_hour*60) + tz_min)*60);
+}
+
+/**
+ * return local (browser) timezone as offset from UTC in seconds
+ *
+ * @returns {Number} offset from UTC in seconds for local timezone
+ */
+function localTimezoneOffset() {
+	// getTimezoneOffset returns the time-zone offset from UTC,
+	// in _minutes_, for the current locale
+	return ((new Date()).getTimezoneOffset() * -60);
+}
+
+/**
+ * return local (browser) timezone as numeric timezone '(+|-)HHMM'
+ *
+ * @returns {String} locat timezone as -/+ZZZZ
+ */
+function localTimezoneInfo() {
+	var tzOffsetMinutes = (new Date()).getTimezoneOffset() * -1;
+
+	return formatTimezoneInfo(0, tzOffsetMinutes);
+}
+
+
+/**
+ * Parse RFC-2822 date into a Unix timestamp (into epoch)
+ *
+ * @param {String} date: date in RFC-2822 format, e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
+ * @returns {Number} epoch i.e. seconds since '00:00:00 1970-01-01 UTC'
+ */
+function parseRFC2822Date(date) {
+	// Date.parse accepts the IETF standard (RFC 1123 Section 5.2.14 and elsewhere)
+	// date syntax, which is defined in RFC 2822 (obsoletes RFC 822)
+	// and returns number of _milli_seconds since January 1, 1970, 00:00:00 UTC
+	return Date.parse(date) / 1000;
+}
+
+
+/* ............................................................ */
+/* formatting date */
+
+/**
+ * format timezone offset as numerical timezone '(+|-)HHMM' or '(+|-)HH:MM'
+ *
+ * @param {Number} hours:    offset in hours, e.g. 2 for '+0200'
+ * @param {Number} [minutes] offset in minutes, e.g. 30 for '-4030';
+ *                           it is split into hours if not 0 <= minutes < 60,
+ *                           for example 1200 would give '+0100';
+ *                           defaults to 0
+ * @param {String} [sep] separator between hours and minutes part,
+ *                       default is '', might be ':' for W3CDTF (rfc-3339)
+ * @returns {String} timezone in '(+|-)HHMM' or '(+|-)HH:MM' format
+ */
+function formatTimezoneInfo(hours, minutes, sep) {
+	minutes = minutes || 0; // to be able to use formatTimezoneInfo(hh)
+	sep = sep || ''; // default format is +/-ZZZZ
+
+	if (minutes < 0 || minutes > 59) {
+		hours = minutes > 0 ? Math.floor(minutes / 60) : Math.ceil(minutes / 60);
+		minutes = Math.abs(minutes - 60*hours); // sign of minutes is sign of hours
+		// NOTE: this works correctly because there is no UTC-00:30 timezone
+	}
+
+	var tzSign = hours >= 0 ? '+' : '-';
+	if (hours < 0) {
+		hours = -hours; // sign is stored in tzSign
+	}
+
+	return tzSign + padLeft(hours, 2, '0') + sep + padLeft(minutes, 2, '0');
+}
+
+/**
+ * return date in local time formatted in iso-8601 like format
+ * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @returns {String} date in local time in iso-8601 like format
+ */
+function formatDateISOLocal(epoch, timezoneInfo) {
+	// date corrected by timezone
+	var localDate = new Date(1000 * (epoch +
+		timezoneOffset(timezoneInfo)));
+	var localDateStr = // e.g. '2005-08-07'
+		localDate.getUTCFullYear()                 + '-' +
+		padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +
+		padLeft(localDate.getUTCDate(),    2, '0');
+	var localTimeStr = // e.g. '21:49:46'
+		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+		padLeft(localDate.getUTCSeconds(), 2, '0');
+
+	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+/**
+ * return date in local time formatted in rfc-2822 format
+ * e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'
+ *
+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'
+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'
+ * @param {Boolean} [padDay] e.g. 'Sun, 07 Aug' if true, 'Sun, 7 Aug' otherwise
+ * @returns {String} date in local time in rfc-2822 format
+ */
+function formatDateRFC2882(epoch, timezoneInfo, padDay) {
+	// A short textual representation of a month, three letters
+	var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+	// A textual representation of a day, three letters
+	var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+	// date corrected by timezone
+	var localDate = new Date(1000 * (epoch +
+		timezoneOffset(timezoneInfo)));
+	var localDateStr = // e.g. 'Sun, 7 Aug 2005' or 'Sun, 07 Aug 2005'
+		days[localDate.getUTCDay()] + ', ' +
+		(padDay ? padLeft(localDate.getUTCDate(),2,'0') : localDate.getUTCDate()) + ' ' +
+		months[localDate.getUTCMonth()] + ' ' +
+		localDate.getUTCFullYear();
+	var localTimeStr = // e.g. '21:49:46'
+		padLeft(localDate.getUTCHours(),   2, '0') + ':' +
+		padLeft(localDate.getUTCMinutes(), 2, '0') + ':' +
+		padLeft(localDate.getUTCSeconds(), 2, '0');
+
+	return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;
+}
+
+/* end of datetime.js */
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (3 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH 04/11] gitweb.js: Extract and improve datetime handling Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists) Jakub Narebski
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

This file provides functions for setting, getting and deleting
cookies.

Code taken from subsection "Cookies in JavaScript" of "Professional
JavaScript for Web Developers" by Nicholas C. Zakas and from cookie
plugin for jQuery (dual licensed under the MIT and GPL licenses).

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Instead of setCookieExp and setCookie (with different behaviors) in
J.H. patch, use single setCookie function.  The code in this patch
consistently assumes that the name of cookie doesn't need to be
escaped, and that value of cookie needs to escaped / unescaped
(using encodeURIComponent rather than deprecated escape).

Because remembering which positional parameter (beyond name and value
of cookie) corresponds to which cookie attribute often requires
checking the code, follow convention used in jQery cookie plugin of
using object literal to implement "named parameters".

Note that J.H.'s gitweb/static/js/cookies.js had a few errors in its
implementation.


P.S. The summary line (subject) for this commit could be better.

 gitweb/Makefile                 |    1 +
 gitweb/static/js/lib/cookies.js |  114 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+), 0 deletions(-)
 create mode 100644 gitweb/static/js/lib/cookies.js

diff --git a/gitweb/Makefile b/gitweb/Makefile
index 403265a..7dd1dee 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -118,6 +118,7 @@ GITWEB_FILES += static/git-logo.png static/git-favicon.png
 # then the rest of files; js/gitweb.js should be last (if it exists)
 GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
 GITWEB_JSLIB_FILES += static/js/lib/datetime.js
+GITWEB_JSLIB_FILES += static/js/lib/cookies.js
 GITWEB_JSLIB_FILES += static/js/javascript-detection.js
 GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 
diff --git a/gitweb/static/js/lib/cookies.js b/gitweb/static/js/lib/cookies.js
new file mode 100644
index 0000000..72b51cd
--- /dev/null
+++ b/gitweb/static/js/lib/cookies.js
@@ -0,0 +1,114 @@
+/**
+ * @fileOverview Accessing cookies from JavaScript
+ * @license GPLv2 or later
+ */
+
+/*
+ * Based on subsection "Cookies in JavaScript" of "Professional
+ * JavaScript for Web Developers" by Nicholas C. Zakas and cookie
+ * plugin from jQuery (dual licensed under the MIT and GPL licenses)
+ */
+
+
+/**
+ * Create a cookie with the given name and value,
+ * and other optional parameters.
+ *
+ * @example
+ *   setCookie('foo', 'bar'); // will be deleted when browser exits
+ *   setCookie('foo', 'bar', { expires: new Date(Date.parse('Jan 1, 2012')) });
+ *   setCookie('foo', 'bar', { expires: 7 }); // 7 days = 1 week
+ *   setCookie('foo', 'bar', { expires: 14, path: '/' });
+ *
+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores).
+ * @param {String} sValue:   The string value stored in a cookie.
+ * @param {Object} [options] An object literal containing key/value pairs
+ *                           to provide optional cookie attributes.
+ * @param {String|Number|Date} [options.expires] Either literal string to be used as cookie expires,
+ *                            or an integer specifying the expiration date from now on in days,
+ *                            or a Date object to be used as cookie expiration date.
+ *                            If a negative value is specified or a date in the past),
+ *                            the cookie will be deleted.
+ *                            If set to null or omitted, the cookie will be a session cookie
+ *                            and will not be retained when the the browser exits.
+ * @param {String} [options.path] Restrict access of a cookie to particular directory
+ *                               (default: path of page that created the cookie).
+ * @param {String} [options.domain] Override what web sites are allowed to access cookie
+ *                                  (default: domain of page that created the cookie).
+ * @param {Boolean} [options.secure] If true, the secure attribute of the cookie will be set
+ *                                   and the cookie would be accessible only from secure sites
+ *                                   (cookie transmission will require secure protocol like HTTPS).
+ */
+function setCookie(sName, sValue, options) {
+	options = options || {};
+	if (sValue === null) {
+		sValue = '';
+		option.expires = 'delete';
+	}
+
+	var sCookie = sName + '=' + encodeURIComponent(sValue);
+
+	if (options.expires) {
+		var oExpires = options.expires, sDate;
+		if (oExpires === 'delete') {
+			sDate = 'Thu, 01 Jan 1970 00:00:00 GMT';
+		} else if (typeof oExpires === 'string') {
+			sDate = oExpires;
+		} else {
+			var oDate;
+			if (typeof oExpires === 'number') {
+				oDate = new Date();
+				oDate.setTime(oDate.getTime() + (oExpires * 24 * 60 * 60 * 1000)); // days to ms
+			} else {
+				oDate = oExpires;
+			}
+			sDate = oDate.toGMTString();
+		}
+		sCookie += '; expires=' + sDate;
+	}
+
+	if (options.path) {
+		sCookie += '; path=' + (options.path);
+	}
+	if (options.domain) {
+		sCookie += '; domain=' + (options.domain);
+	}
+	if (options.secure) {
+		sCookie += '; secure';
+	}
+	document.cookie = sCookie;
+}
+
+/**
+ * Get the value of a cookie with the given name.
+ *
+ * @param {String} sName: Unique name of a cookie (letters, numbers, underscores)
+ * @returns {String|null} The string value stored in a cookie
+ */
+function getCookie(sName) {
+	var sRE = '(?:; )?' + sName + '=([^;]*);?';
+	var oRE = new RegExp(sRE);
+	if (oRE.test(document.cookie)) {
+		return decodeURIComponent(RegExp['$1']);
+	} else {
+		return null;
+	}
+}
+
+/**
+ * Delete cookie with given name
+ *
+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores)
+ * @param {Object} [options] An object literal containing key/value pairs
+ *                           to provide optional cookie attributes.
+ * @param {String} [options.path]   Must be the same as when setting a cookie
+ * @param {String} [options.domain] Must be the same as when setting a cookie
+ */
+function deleteCookie(sName, options) {
+	options = options || {};
+	options.expires = 'delete';
+
+	setCookie(sName, '', options);
+}
+
+/* end of cookies.js */
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists)
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (4 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html Jakub Narebski
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

The code is simplified and does not support full specification of
native getElementsByClassName method, but implements just subset that
would be enough for gitweb, supporting only single class name.

Signed-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Instead of providing findElementsByClassName function like in
J.H. patch, which could use getElementsByClassName if present (but
didn't cache check for document.getElementsByClassName), this version
adds getElementsByClassName method to 'document' if it doesn't exist.

Which solution is better / easier to understand?  I'm not sure.

 gitweb/static/js/lib/common-lib.js |   51 ++++++++++++++++++++++++++++++++++++
 1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index d6b0c0d..b371391 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -89,6 +89,57 @@ function createRequestObject() {
 
 
 /* ............................................................ */
+/* Support for legacy browsers */
+
+/**
+ * Provides getElementsByClassName method, if there is no native
+ * implementation of this method.
+ *
+ * NOTE that there are limits and differences compared to native
+ * getElementsByClassName as defined by e.g.:
+ *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName
+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
+ *
+ * Namely, this implementation supports only single class name as
+ * argument and not set of space-separated tokens representing classes,
+ * it returns Array of nodes rather than live NodeList, and has
+ * additional optional argument where you can limit search to given tags
+ * (via getElementsByTagName).
+ *
+ * Based on
+ *   http://code.google.com/p/getelementsbyclassname/
+ *   http://www.dustindiaz.com/getelementsbyclass/
+ *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
+ *
+ * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
+ *
+ * @param {String} class: name of _single_ class to find
+ * @param {String} [taghint] limit search to given tags
+ * @returns {Node[]} array of matching elements
+ */
+if (!('getElementsByClassName' in document)) {
+	document.getElementsByClassName = function (classname, taghint) {
+		taghint = taghint || "*";
+		var elements = (taghint === "*" && document.all) ?
+		               document.all :
+		               document.getElementsByTagName(taghint);
+		var pattern = new RegExp("(^|\\s)" + classname + "(\\s|$)");
+		var matches= [];
+		for (var i = 0, j = 0, n = elements.length; i < n; i++) {
+			var el= elements[i];
+			if (el.className && pattern.test(el.className)) {
+				// matches.push(el);
+				matches[j] = el;
+				j++;
+			}
+		}
+		return matches;
+	};
+} // end if
+
+
+/* ............................................................ */
 /* unquoting/unescaping filenames */
 
 /**#@+
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (5 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists) Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [RFC/PATCH 08/11] gitweb: Unify the way long timestamp is displayed Jakub Narebski
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

It is pure refactoring and doesn't change gitweb output, though this
could potentially affect 'summary', 'log', and 'commit'-like views
('commit', 'commitdiff', 'tag').

Remove print_local_time and format_local_time, as their use is now
replaced (indirectly) by using format_timestamp_html.


While at it improve whitespace formatting.

Inspired-by-code-by: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This helper subroutine would reduce a bit code repetition that can be
found in original J.H. patch.

 gitweb/gitweb.perl |   45 ++++++++++++++++++++++-----------------------
 1 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index ee69ea6..7329db2 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3938,22 +3938,21 @@ sub git_print_section {
 	print $cgi->end_div;
 }
 
-sub print_local_time {
-	print format_local_time(@_);
-}
+sub format_timestamp_html {
+	my ($date, %opts) = @_;
+	my $strtime = $date->{'rfc2822'};
 
-sub format_local_time {
-	my $localtime = '';
-	my %date = @_;
-	if ($date{'hour_local'} < 6) {
-		$localtime .= sprintf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-			$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
-	} else {
-		$localtime .= sprintf(" (%02d:%02d %s)",
-			$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+	return $strtime unless $opts{'-localtime'};
+
+	my $localtime_format = '(%02d:%02d %s)';
+	if ($date->{'hour_local'} < 6) {
+		$localtime_format = '(<span class="atnight">%02d:%02d</span> %s)';
 	}
+	$strtime .= ' ' .
+	            sprintf($localtime_format,
+	                    $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'});
 
-	return $localtime;
+	return $strtime;
 }
 
 # Outputs the author name and date in long form
@@ -3966,10 +3965,9 @@ sub git_print_authorship {
 	my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
 	print "<$tag class=\"author_date\">" .
 	      format_search_author($author, "author", esc_html($author)) .
-	      " [$ad{'rfc2822'}";
-	print_local_time(%ad) if ($opts{-localtime});
-	print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
-		  . "</$tag>\n";
+	      " [".format_timestamp_html(\%ad, %opts)."]".
+	      git_get_avatar($co->{'author_email'}, -pad_before => 1) .
+	      "</$tag>\n";
 }
 
 # Outputs table rows containing the full author or committer information,
@@ -3986,16 +3984,16 @@ sub git_print_authorship_rows {
 		my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
 		print "<tr><td>$who</td><td>" .
 		      format_search_author($co->{"${who}_name"}, $who,
-			       esc_html($co->{"${who}_name"})) . " " .
+		                           esc_html($co->{"${who}_name"})) . " " .
 		      format_search_author($co->{"${who}_email"}, $who,
-			       esc_html("<" . $co->{"${who}_email"} . ">")) .
+		                           esc_html("<" . $co->{"${who}_email"} . ">")) .
 		      "</td><td rowspan=\"2\">" .
 		      git_get_avatar($co->{"${who}_email"}, -size => 'double') .
 		      "</td></tr>\n" .
 		      "<tr>" .
-		      "<td></td><td> $wd{'rfc2822'}";
-		print_local_time(%wd);
-		print "</td>" .
+		      "<td></td><td>" .
+		      format_timestamp_html(\%wd, -localtime=>1) .
+		      "</td>" .
 		      "</tr>\n";
 	}
 }
@@ -5410,7 +5408,8 @@ sub git_summary {
 	      "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
 	      "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
 	if (defined $cd{'rfc2822'}) {
-		print "<tr id=\"metadata_lchange\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n";
+		print "<tr id=\"metadata_lchange\"><td>last change</td>" .
+		      "<td>".format_timestamp_html(\%cd)."</td></tr>\n";
 	}
 
 	# use per project git URL list in $projectroot/$project/cloneurl
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC/PATCH 08/11] gitweb: Unify the way long timestamp is displayed
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (6 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone Jakub Narebski
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

format_timestamp_html loses its "-localtime => 1" option, and now
always print the local time (in author/comitter/tagger local
timezone), with "atnight" warning if needed.

This means that both 'summary' and 'log' views now display localtime.
In the case of 'log' view this can be thought as an improvement, as
now one can easily see which commits in a series are made "atnight"
and should be examined closer.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Not really necessary, though dropping it from series would probably
require resolving trivial spurious textual conflicts.

It is marked as an RFC because I am not quite satisfied with the way
'log' view looks like after this change, though OTOH this change is an
improvement for 'log' view...

 gitweb/gitweb.perl |    8 +++-----
 1 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 7329db2..67bcfe8 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3939,11 +3939,9 @@ sub git_print_section {
 }
 
 sub format_timestamp_html {
-	my ($date, %opts) = @_;
+	my $date = shift;
 	my $strtime = $date->{'rfc2822'};
 
-	return $strtime unless $opts{'-localtime'};
-
 	my $localtime_format = '(%02d:%02d %s)';
 	if ($date->{'hour_local'} < 6) {
 		$localtime_format = '(<span class="atnight">%02d:%02d</span> %s)';
@@ -3965,7 +3963,7 @@ sub git_print_authorship {
 	my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
 	print "<$tag class=\"author_date\">" .
 	      format_search_author($author, "author", esc_html($author)) .
-	      " [".format_timestamp_html(\%ad, %opts)."]".
+	      " [".format_timestamp_html(\%ad)."]".
 	      git_get_avatar($co->{'author_email'}, -pad_before => 1) .
 	      "</$tag>\n";
 }
@@ -3992,7 +3990,7 @@ sub git_print_authorship_rows {
 		      "</td></tr>\n" .
 		      "<tr>" .
 		      "<td></td><td>" .
-		      format_timestamp_html(\%wd, -localtime=>1) .
+		      format_timestamp_html(\%wd) .
 		      "</td>" .
 		      "</tr>\n";
 	}
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (7 preceding siblings ...)
  2011-04-09 22:49 ` [RFC/PATCH 08/11] gitweb: Unify the way long timestamp is displayed Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>

This patch is based on Kevin Cernekee's <cernekee@gmail.com>
patch series entitled "gitweb: introduce localtime feature".  While
Kevin's patch changed the server side output so that the timezone
was output from gitweb itself, this has a number of drawbacks, in
particular with respect to gitweb-caching.

This patch takes the same basic goal, display the appropriate times in
a given common timezone, and implements it in JavaScript.  This
requires adding / using a new class, "datetime", to be able to find
elements to be adjusted from JavaScript.  Appropriate dates are
wrapped in a span with this class.

Timezone to be used can be retrieved from "gitweb_tz" cookie, though
currently there is no way to set / manipulate this cookie from gitweb;
this is left for later commit.

Valid timezones, currently, are: "utc", "local" (which means that
timezone is taken from browser), and "+/-ZZZZ" numeric timezone as in
RFC-2822.  Default timezone is "local" (currently not configurable,
left for later commit... though one could edit browser cookie externally).

Fallback (should JavaScript not be enabled) is to treat dates as they
have been and display them, only, in UTC.

Pages affected:
* 'summary' view, "last change" field (commit time from latest change)
* 'log' view, author time
* 'commit' and 'commitdiff' views, author/committer time
* 'tag' view, tagger time

Based-on-code-from: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This is major part of J.H. patch.

NOTE that while major ideas (and some of names) are taken from
J.H. patch, the code was practically written anew from scratch.

I avoid using global variables, as recommended in various tips for
better JavaScript performance.  Global variables are discouraged
anyway...


Actual interface for changing / selecting timezone is left for later
commit, both because smaller patches are easier to review, and because
interface part is more tricky and involves more issue.

 gitweb/Makefile                     |    1 +
 gitweb/gitweb.perl                  |   11 +++++--
 gitweb/static/js/adjust-timezone.js |   59 +++++++++++++++++++++++++++++++++++
 gitweb/static/js/lib/datetime.js    |   15 +++++++++
 4 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 gitweb/static/js/adjust-timezone.js

diff --git a/gitweb/Makefile b/gitweb/Makefile
index 7dd1dee..5d20515 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -120,6 +120,7 @@ GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
 GITWEB_JSLIB_FILES += static/js/lib/datetime.js
 GITWEB_JSLIB_FILES += static/js/lib/cookies.js
 GITWEB_JSLIB_FILES += static/js/javascript-detection.js
+GITWEB_JSLIB_FILES += static/js/adjust-timezone.js
 GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 
 
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 67bcfe8..6651946 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3732,9 +3732,14 @@ sub git_footer_html {
 		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
 		      qq!           "!. href() .qq!");\n!.
 		      qq!</script>\n!;
-	} elsif (gitweb_check_feature('javascript-actions')) {
+	} else {
 		print qq!<script type="text/javascript">\n!.
-		      qq!window.onload = fixLinks;\n!.
+		      qq!window.onload = function () {\n!.
+		      (gitweb_check_feature('javascript-actions') ?
+		      qq!	fixLinks();\n! : '').
+		      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html
+		      qq!	onloadTZSetup('local', 'gitweb_tz', 'datetime');\n!.
+		      qq!};\n!.
 		      qq!</script>\n!;
 	}
 
@@ -3940,7 +3945,7 @@ sub git_print_section {
 
 sub format_timestamp_html {
 	my $date = shift;
-	my $strtime = $date->{'rfc2822'};
+	my $strtime = '<span class="datetime">'.$date->{'rfc2822'}.'</span>';
 
 	my $localtime_format = '(%02d:%02d %s)';
 	if ($date->{'hour_local'} < 6) {
diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js
new file mode 100644
index 0000000..5cebc08
--- /dev/null
+++ b/gitweb/static/js/adjust-timezone.js
@@ -0,0 +1,59 @@
+// 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 and adjust dates to use this common timezone.
+ *
+ * This function is called during onload event (added to window.onload).
+ *
+ * @param {String} tzDefault: default timezone, if there is no cookie
+ * @param {String} tzCookieName: name of cookie to store timezone
+ * @param {String} tzClassName: denotes elements with date to be adjusted
+ */
+function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {
+	var tzCookie = getCookie(tzCookieName);
+	var tz = tzCookie ? tzCookie : tzDefault;
+
+	// server-side of gitweb produces datetime in UTC,
+	// so if tz is 'utc' there is no need for changes
+	if (tz !== 'utc') {
+		fixDatetimeTZ(tz, tzClassName);
+	}
+}
+
+
+/**
+ * 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 'utf', or 'local'
+ * @param {String} tzClassName: specifies elements to be changed
+ */
+function fixDatetimeTZ(tz, tzClassName) {
+	// 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];
+
+		var epoch = parseRFC2822Date(curElement.innerHTML);
+		var adjusted = formatDateRFC2882(epoch, tz);
+
+		// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2
+		curElement.firstChild.data = adjusted;
+	}
+}
+
+/* end of adjust-timezone.js */
diff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js
index b3dcedb..f78c60a 100644
--- a/gitweb/static/js/lib/datetime.js
+++ b/gitweb/static/js/lib/datetime.js
@@ -105,6 +105,21 @@ function formatTimezoneInfo(hours, minutes, sep) {
 }
 
 /**
+ * translate 'utc' and 'local' to numerical timezone
+ * @param {String} timezoneInfo: might be 'utc' or 'local' (browser)
+ */
+function normalizeTimezoneInfo(timezoneInfo) {
+	switch (timezoneInfo) {
+	case 'utc':
+		return '+0000';
+	case 'local': // 'local' is browser timezone
+		return localTimezoneInfo();
+	}
+	return timezoneInfo;
+}
+
+
+/**
  * return date in local time formatted in iso-8601 like format
  * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
  *
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (8 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-10  9:36   ` Jakub Narebski
  2011-04-10 15:10   ` Jakub Narebski
  2011-04-09 22:49 ` [PATCH/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable Jakub Narebski
  2011-04-12  1:19 ` [PATCH 00/11] gitweb: Change timezone Kevin Cernekee
  11 siblings, 2 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>

This will modify HTML, add CSS rules and add DOM event handlers so
that clicking on any date (the common part, not the localtime part)
will display a drop down menu to choose the timezone to change to.

Currently menu displays only the following timezones:

  utc
  local
  -1200
  -1100
  ...
  +1100
  +1200
  +1300
  +1400

In timezone selection menu each timezone is +1hr to the previous.  The
code is capable of handling fractional timezones, but those have not
been added to the menu.

All changes are saved to a cookie, so page changes and closing /
reopening browser retains the last known timezone setting used.

[jn: Changed from innerHTML to DOM, moved to event delegation for
onclick to trigger menu, added close button and cookie refreshing]

KNOWN BUGS:
===========
* In 'log' view menu is generated on the right side of whole page,
  instead of at near date, as is the case with 'summary', 'commit',
  'commitdiff', and 'tag' views.

* Replacing inline style for 'popup' and 'close-button' elements of
  timezone menu with style defined in gitweb.css results in menu being
  put between datetime and its local part, as if absolute positioning
  didn't remove element from the flow.

  Currently style is duplicated in gitweb.css (in CSS file) and in
  <element>.style.cssText (in JavaScript).

* 'close-button' is not placed correctly if it uses 'float: right;'
  style (problem floating element in absolutely positioned box, even
  though there is no problem if containing box is has position: fixed).

  Therefore it was necessary to use absolute positioning for close
  button.  This might cause for it to overlay 'Select timezone:' text
  in some cases.

KNOWN ISSUES:
=============
* It is not easy to discover how to change timezone of dates in gitweb
  output... though the same can be said for other parts of gitweb output.
  Also marking of dates might be a bit misleading.

* Markup should probably change when timezone selection menu is displayed.
  Timezone UI could also be improved.

Signed-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Changes from J.H. patch:
~~~~~~~~~~~~~~~~~~~~~~~~
* In J.H. patch to display timezone menu you had to click small "+"
  beside date, that was added for each date using JavaScript.

  In this patch to display timezone menu you have to click anywhere
  on date, as hinted by title='Click to change timezone' added to
  those elements via JavaScript, and link-like view on :hover
  via CSS added with JavaScript.

* In J.H. patch clicking on "+" was handled with setting onclick for
  each such element:

    <span onclick="clickDate(event.target);" title="+">+</span>

  once for each date (but only for 'log' view there are many dates).

  In this patch clicking on dates is handled via event delegation
  (event capturing for 'document').

* In J.H. patch timezone menu was generated from scratch on each
  click on unopened "+" (which changed to "-" and handled closing)
  by setting target.innerHTML to string generated by concatenation.

  In this patch timezone menu is generated once on page load, and
  only attached on clicking on date.  This menu is generated using
  DOM methods rather than innerHTML -- this is because my ancient
  browser (Mozilla 1.17.12 Gecko/20050923) considers innerHTML
  non-standard with strict mode (XHTML + application/xhtml+xml).

* In J.H. patch to remove timezone menu without selection you had to
  click on "-" (turning it again into "+").

  In this patch to remove timezone menu you had to click '[x]' button
  marked with title='(click on this box to close)'.

* In J.H. patch timezone menu was composed of table.  In this patch it
  is simply <div> element.

* In J.H. patch to get date gitweb JavaScript parsed title attribute.
  In this patch to get date gitweb JavaScript parses contents of element.

On one hand side with current code is hopefully easier to understand
code flow.  On the other hand it uses much more advanced features of
JavaScript: DOM (mainly DOM HTML, with bits of CSS, Style and Core),
event delegation, closures, absolute positioning / visual model.

 gitweb/static/gitweb.css            |   25 ++++
 gitweb/static/js/adjust-timezone.js |  242 +++++++++++++++++++++++++++++++++--
 gitweb/static/js/lib/common-lib.js  |   27 ++++-
 3 files changed, 283 insertions(+), 11 deletions(-)

diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index 79d7eeb..6d17e12 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -579,6 +579,31 @@ div.remote {
 	display: inline-block;
 }
 
+/* JavaScript-base timezone manipulation */
+
+.popup { // timezone selection UI
+	position: absolute;
+	top: 0; right: 0;
+	border: 1px solid;
+	background-color: #f0f0f0;
+}
+
+.close-button { // close timezone selection UI without selecting
+	//float: right; // float doesn't work within absolutely positioned container
+	position: absolute;
+	top: 0px; right: 0px;
+	border:  1px solid green;
+	margin:  1px 1px 1px 1px;
+	padding: 1px 1px 1px 1px;
+	width:     10px;
+	height:    10px;
+	font-size:  8px;
+	font-weight: bold;
+	text-align: center;
+	background-color: #fff0f0;
+}
+
+
 /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
 
 /* Highlighting theme definition: */
diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js
index 5cebc08..6ac32ca 100644
--- a/gitweb/static/js/adjust-timezone.js
+++ b/gitweb/static/js/adjust-timezone.js
@@ -7,7 +7,8 @@
  */
 
 /**
- * Get common timezone and adjust dates to use this common timezone.
+ * 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).
  *
@@ -17,13 +18,20 @@
  */
 function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {
 	var tzCookie = getCookie(tzCookieName);
-	var tz = tzCookie ? tzCookie : tzDefault;
+	var tz = tzDefault;
 
-	// server-side of gitweb produces datetime in UTC,
-	// so if tz is 'utc' there is no need for changes
-	if (tz !== 'utc') {
-		fixDatetimeTZ(tz, tzClassName);
+	if (tzCookie) {
+		// set timezone to value saved in a cookie
+		tz = tzCookie;
+		// refresh cookie, so it expires 7 days from last use of gitweb
+		setCookie(tzCookieName, tzCookie, { expires: 7, path: '/' });
 	}
+
+	// add UI for changing timezone
+	addChangeTZ(tz, tzCookieName, tzClassName);
+
+	// adjust dates to use specified common timezone
+	fixDatetimeTZ(tz, tzClassName);
 }
 
 
@@ -40,6 +48,10 @@ function fixDatetimeTZ(tz, tzClassName) {
 		return;
 	}
 
+	// server-side of gitweb produces datetime in UTC,
+	// so if tz is 'utc' there is no need for changes
+	var nochange = tz === 'utc';
+
 	// translate to timezone in '(-|+)HHMM' format
 	tz = normalizeTimezoneInfo(tz);
 
@@ -48,12 +60,222 @@ function fixDatetimeTZ(tz, tzClassName) {
 	for (var i = 0, len = classesFound.length; i < len; i++) {
 		var curElement = classesFound[i];
 
-		var epoch = parseRFC2822Date(curElement.innerHTML);
-		var adjusted = formatDateRFC2882(epoch, tz);
+		curElement.title = 'Click to change timezone';
+		if (!nochange) {
+			var epoch = parseRFC2822Date(curElement.innerHTML);
+			var adjusted = formatDateRFC2882(epoch, tz);
+
+			// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2
+			curElement.firstChild.data = adjusted;
+		}
+	}
+}
+
+/**
+ * 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 instals appropriate onclick trigger (via event delegation).
+ *
+ * @param {String} tzSelected: pre-selected timezone,
+ *                             'utf' or 'local' or '(-|+)HHMM'
+ * @param {String} tzCookieName: name of cookie to store selection
+ * @param {String} tzClassName: specifies elements to install trigger
+ */
+function addChangeTZ(tzSelected, tzCookieName, 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, tzCookieName, 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:
+			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 {String} tzCookieName: name of cookie to save result of selection
+ * @returns {DocumentFragment}
+ */
+function createChangeTZForm(documentFragment, tzSelected, tzCookieName, tzClassName) {
+	var div = document.createElement("div");
+	div.className = 'popup';
+	///*
+	div.style.cssText =
+		'position: absolute; top: 0; right: 0; ' +
+		'border: 1pt solid; background-color: #f0f0f0;';
+	//*/
+
+	var closeButton = document.createElement('div');
+	closeButton.className = 'close-button';
+	///*
+	closeButton.style.cssText =
+		//'float: right; '+ // float doesn't work within absolutely positioned container
+		'position: absolute; top: 0px; right: 0px; '+
+		'border: 1px solid green; margin: 1px 1px 1px 1px; '+
+		'background-color: #fff0f0; font-weight: bold; font-size: 8px; text-align: center; ' +
+		'width: 10px; height: 10px; padding: 1px 1px 1px 1px;';
+	//*/
+	closeButton.title = '(click on this box to close)';
+	closeButton.appendChild(document.createTextNode('X'));
+	closeButton.onclick = function onclickCloseTZForm(event) {
+		event = event || window.event;
+		var target = event.target || event.srcElement;
+
+		removeChangeTZForm(documentFragment, target, tzClassName);
+	};
+	div.appendChild(closeButton);
+
+	div.appendChild(document.createTextNode('Select timezone: '));
+	var br = document.createElement('br');
+	br.clear = 'all';
+	div.appendChild(br);
+
+	var select = document.createElement("select");
+	select.name = "tzoffset";
+	//select.style.clear = 'all';
+
+	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));
+
+		select.appendChild(option);
+	}
+
+	select.onchange = function onTZFormChange(event) {
+		event = event || window.event;
+		var target = event.target || event.srcElement;
 
-		// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2
-		curElement.firstChild.data = adjusted;
+		removeChangeTZForm(documentFragment, target, tzClassName);
+
+		var selected = target.options.item(target.selectedIndex);
+		if (selected) {
+			selected.defaultSelected = true;
+			setCookie(tzCookieName, selected.value, { expires: 7, path: '/' });
+			fixDatetimeTZ(selected.value, tzClassName);
+		}
+	};
+	// NOTE: onblur removal might be not necessary with close button
+	// NOTE: the same function (closure) as for closeButton.onclick
+	select.onblur = function onTZFormBlur(event) {
+		event = event || window.event;
+		var target = event.target || event.srcElement;
+
+		removeChangeTZForm(documentFragment, target, 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
+	var container = target.parentNode;
+	while (container &&
+	       container.className !== tzClassName) {
+		container = container.parentNode;
+	}
+	// safety check if we found correct container,
+	// and if it isn't deleted already
+	if (!container ||
+	    container.className !== tzClassName ||
+	    container.lastChild.className !== 'popup') {
+		return documentFragment;
+	}
+
+	// timezone selection UI was appended as last child
+	// see also displayChangeTZForm function
+	var removed = container.removeChild(container.lastChild);
+	if (documentFragment.firstChild !== removed) {
+		// re-append it so it would be available for next time
+		documentFragment.appendChild(removed);
+	}
+	container.style.removeProperty('position');
+	if (!container.style.cssText) {
+		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';
+
+	// show/display UI for changing timezone
+	target.appendChild(tzSelectFragment);
+}
+
+
+/**
+ * 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&plusmn;00:00'
+		}
+	}
+
+	return timezones;
 }
 
 /* end of adjust-timezone.js */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index b371391..faff9b6 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -64,7 +64,7 @@ function padLeft(input, width, ch) {
 
 
 /* ............................................................ */
-/* Ajax */
+/* Handling browser incompatibilities */
 
 /**
  * Create XMLHttpRequest object in cross-browser way
@@ -88,6 +88,31 @@ function createRequestObject() {
 }
 
 
+/**
+ * Insert rule giving specified STYLE to given SELECTOR at the end of
+ * first CSS stylesheet.
+ *
+ * @param {String} selector: CSS selector, e.g. '.class'
+ * @param {String} style: rule contents, e.g. 'background-color: red;'
+ */
+function addCssRule(selector, style) {
+	var stylesheet = document.styleSheets[0];
+
+	var theRules = [];
+	if (stylesheet.cssRules) {     // W3C way
+		theRules = stylesheet.cssRules;
+	} else if (stylesheet.rules) { // IE way
+		theRules = stylesheet.rules;
+	}
+
+	if (stylesheet.insertRule) {    // W3C way
+		stylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length);
+	} else if (stylesheet.addRule) { // IE way
+		stylesheets.addRule(selector, style);
+	}
+}
+
+
 /* ............................................................ */
 /* Support for legacy browsers */
 
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (9 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
@ 2011-04-09 22:49 ` Jakub Narebski
  2011-04-12  1:19 ` [PATCH 00/11] gitweb: Change timezone Kevin Cernekee
  11 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-09 22:49 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

Configure JavaScript-based ability to select common timezone for git
dates via %feature mechanism, namely 'javascript-timezone' feature.

The following settings are configurable:
* default timezone (defaults to 'local' i.e. browser timezone);
  this also can function as a way to disable this ability,
  by setting it to false-ish value (undef or '')
* name of cookie to store user's choice of timezone
* class name to mark dates


NOTE: This is a bit of abuse of %feature system, which can store only
sequence of values, rather than dictionary (hash); usually but not
always only a single value is used.

Based-on-code-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Original patch by J.H. had strictly speaking only $jslocaltime
(default timezone) configurable, though cookie name was stored in
global variable (set in gitweb/static/js/common-defs.js).

But do we really need full flexibility...?

 gitweb/gitweb.perl |   37 ++++++++++++++++++++++++++++++-------
 1 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 6651946..b80f2e0 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -480,6 +480,18 @@ our %feature = (
 		'override' => 0,
 		'default' => [0]},
 
+	# Enable and configure ability to change common timezone for dates
+	# in gitweb output via JavaScript.  Enabled by default.
+	# Project specific override is not supported.
+	'javascript-timezone' => {
+		'override' => 0,
+		'default' => [
+			'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,
+			             # or undef to turn off this feature
+			'gitweb_tz', # name of cookie where to store selected timezone
+			'datetime',  # CSS class used to mark up dates for manipulation
+		]},
+
 	# Syntax highlighting support. This is based on Daniel Svensson's
 	# and Sham Chukoury's work in gitweb-xmms2.git.
 	# It requires the 'highlight' program present in $PATH,
@@ -3733,13 +3745,18 @@ sub git_footer_html {
 		      qq!           "!. href() .qq!");\n!.
 		      qq!</script>\n!;
 	} else {
+		my ($jstimezone, $tz_cookie, $datetime_class) =
+			gitweb_get_feature('javascript-timezone');
+
 		print qq!<script type="text/javascript">\n!.
-		      qq!window.onload = function () {\n!.
-		      (gitweb_check_feature('javascript-actions') ?
-		      qq!	fixLinks();\n! : '').
-		      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html
-		      qq!	onloadTZSetup('local', 'gitweb_tz', 'datetime');\n!.
-		      qq!};\n!.
+		      qq!window.onload = function () {\n!;
+		if (gitweb_check_feature('javascript-actions')) {
+			print qq!	fixLinks();\n!;
+		}
+		if ($jstimezone && $tz_cookie && $datetime_class) {
+			print qq!	onloadTZSetup('$jstimezone', '$tz_cookie', '$datetime_class');\n!;
+		}
+		print qq!};\n!.
 		      qq!</script>\n!;
 	}
 
@@ -3945,7 +3962,13 @@ sub git_print_section {
 
 sub format_timestamp_html {
 	my $date = shift;
-	my $strtime = '<span class="datetime">'.$date->{'rfc2822'}.'</span>';
+	my $strtime = $date->{'rfc2822'};
+
+	my (undef, undef, $datetime_class) =
+		gitweb_get_feature('javascript-timezone');
+	if ($datetime_class) {
+		$strtime = qq!<span class="datetime">$strtime</span>!;
+	}
 
 	my $localtime_format = '(%02d:%02d %s)';
 	if ($date->{'hour_local'} < 6) {
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates
  2011-04-09 22:49 ` [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
@ 2011-04-10  9:36   ` Jakub Narebski
  2011-04-10 14:39     ` Jakub Narebski
  2011-04-10 15:10   ` Jakub Narebski
  1 sibling, 1 reply; 20+ messages in thread
From: Jakub Narebski @ 2011-04-10  9:36 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee

Jakub Narebski wrote:

> KNOWN BUGS:
> ===========
> * In 'log' view menu is generated on the right side of whole page,
>   instead of at near date, as is the case with 'summary', 'commit',
>   'commitdiff', and 'tag' views.

I'm not sure if it is behavior expected by CSS visual formatting model
or not: the issue is with absolutely positioned block element

  div.popup { position: absolute; top: 0; right: 0; }

inside relatively positioned _inline_ element

  span.marker { position: relative; }

In this case 'top: 0; right: 0' refers somehow to parent block element
of inline element... at least that's what I think.  Note that also in
the case of 'summary', 'commit', 'commitdiff' and 'tag' views the popup
position is not entirely what one could expect...

What is strange is that 'top: 0; left: 0' works correctly (sic!).
 
> * 'close-button' is not placed correctly if it uses 'float: right;'
>   style (problem floating element in absolutely positioned box, even
>   though there is no problem if containing box is has
>   position: fixed). 
> 
>   Therefore it was necessary to use absolute positioning for close
>   button.  This might cause for it to overlay 'Select timezone:' text
>   in some cases.

This is because browser didn't determine width of '.popup' block element 
at the time when float element is positioned.  Setting explicitly width 
of timezone menu will allow to use float for '.close-button'.

Both normally positioned and fixed positioned block elements have by 
default 100% width, that is why it worked for them.

Can fix in next iteration.
-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates
  2011-04-10  9:36   ` Jakub Narebski
@ 2011-04-10 14:39     ` Jakub Narebski
  0 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-10 14:39 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee

Jakub Narebski wrote:
> Jakub Narebski wrote:
> 
> > KNOWN BUGS:
> > ===========
> > * In 'log' view menu is generated on the right side of whole page,
> >   instead of at near date, as is the case with 'summary', 'commit',
> >   'commitdiff', and 'tag' views.
> 
> I'm not sure if it is behavior expected by CSS visual formatting model
> or not: the issue is with absolutely positioned block element
> 
>   div.popup { position: absolute; top: 0; right: 0; }
> 
> inside relatively positioned _inline_ element
> 
>   span.marker { position: relative; }
> 
> In this case 'top: 0; right: 0' refers somehow to parent block element
> of inline element... at least that's what I think.  Note that also in
> the case of 'summary', 'commit', 'commitdiff' and 'tag' views the popup
> position is not entirely what one could expect...
> 
> What is strange is that 'top: 0; left: 0' works correctly (sic!).

This looks like a bug in Mozilla 1.7.12 (Gecko/20050923) I ordinarily use.
Checking simplified test case with Konqueror 3.5.3 shows correct behavior
for all combinations of absolute positioning, at least when

    span.marker { position: relative; display: inline-block; }

Without "display: inline-block;" the "top: 0; right: 0;" is displaced
compared to the end of marker.


Because vger anti-spam filter doesn't like HTML attachments, you can check
test case here: https://gist.github.com/912389

Try it also with 'display: inline-block;' uncommented.

-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates
  2011-04-09 22:49 ` [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
  2011-04-10  9:36   ` Jakub Narebski
@ 2011-04-10 15:10   ` Jakub Narebski
  1 sibling, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-10 15:10 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee

Jakub Narebski wrote:

> KNOWN BUGS:
> ===========

> * Replacing inline style for 'popup' and 'close-button' elements of
>   timezone menu with style defined in gitweb.css results in menu being
>   put between datetime and its local part, as if absolute positioning
>   didn't remove element from the flow.
> 
>   Currently style is duplicated in gitweb.css (in CSS file) and in
>   <element>.style.cssText (in JavaScript).

I'm very sorry, false alarm.  The error was mine: the CSS in gitweb.css
is incorrect; I forgot that in CSS you have only C-like "/* foo */"
comments, and C++ / JavaScript-like "// ... \n" doesn't work.

See e.g.: http://jigsaw.w3.org/css-validator/validator?uri=http%3A%2F%2Frepo.or.cz%2Fw%2Fgit%2Fjnareb-git.git%2Fblob_plain%2F524656258696381f9e6b9169256688165e21a3f9%3A%2Fgitweb%2Fstatic%2Fgitweb.css&lang=en&profile=css21&usermedium=all&warning=1
  

Will fix.

> diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
> index 79d7eeb..6d17e12 100644
> --- a/gitweb/static/gitweb.css
> +++ b/gitweb/static/gitweb.css
> @@ -579,6 +579,31 @@ div.remote {
>  	display: inline-block;
>  }
>  
> +/* JavaScript-base timezone manipulation */
> +
> +.popup { // timezone selection UI
            ^^

Should be

  +.popup { /* timezone selection UI */

> +	position: absolute;
> +	top: 0; right: 0;
> +	border: 1px solid;
> +	background-color: #f0f0f0;
> +}

-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/11] gitweb: Change timezone
  2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
                   ` (10 preceding siblings ...)
  2011-04-09 22:49 ` [PATCH/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable Jakub Narebski
@ 2011-04-12  1:19 ` Kevin Cernekee
  2011-04-12 12:44   ` Jakub Narebski
  11 siblings, 1 reply; 20+ messages in thread
From: Kevin Cernekee @ 2011-04-12  1:19 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, John 'Warthog9' Hawley

On Sat, Apr 9, 2011 at 3:49 PM, Jakub Narebski <jnareb@gmail.com> wrote:
> This is split version (with assorted cleanups) of J.H. patch adding
> JavaScript-base ability to change timezone in which dates are
> displayed.

Jakub,

Thanks for the update.  This UI does seem to work better than the
original "[+]" dropdown interface.

The new code appears to degrade gracefully on IE6 (everything shown in UTC).

Tests on Firefox 3.6.15 looked OK.

Chromium 6.0.472.62 (59676) does not like this operation:

Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
  removeChangeTZForm
  /gitweb-static/gitweb.js:785
onTZFormChange

line 785: var removed = container.removeChild(container.lastChild);

Opera 10.63 resets target.selectedIndex to 1 after calling
removeChangeTZForm() from onTZFormChange().  Net effect is that you're
always stuck in the "local" zone.  Here is a workaround that fixed it
for me:

       var target = event.target || event.srcElement;
       var selectedIndex = target.selectedIndex;

       removeChangeTZForm(documentFragment, target, tzClassName);

       var selected = target.options.item(selectedIndex);

Couple other random nitpicks:

> +	// server-side of gitweb produces datetime in UTC,
> +	// so if tz is 'utc' there is no need for changes
> +	var nochange = tz === 'utc';

If I delete my gitweb_tz cookie, then refresh the page:

 - All times show up in my browser's local time (good)

 - Clicking UTC/GMT on the dropdown has no immediate effect; I need to
   refresh the page again to see the times change (bad)

> +	my (undef, undef, $datetime_class) =
> +		gitweb_get_feature('javascript-timezone');
> +	if ($datetime_class) {
> +		$strtime = qq!<span class="datetime">$strtime</span>!;

Should this hard-code class "datetime", or use $datetime_class from
the gitweb server configuration?

> +/* JavaScript-base timezone manipulation */

Might want to reword as "JavaScript-based"

> +		// refresh cookie, so it expires 7 days from last use of gitweb
> +		setCookie(tzCookieName, tzCookie, { expires: 7, path: '/' });

Hmm, only 7 days?

> + * and instals appropriate onclick trigger (via event delegation).

"installs"

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/11] gitweb: Change timezone
  2011-04-12  1:19 ` [PATCH 00/11] gitweb: Change timezone Kevin Cernekee
@ 2011-04-12 12:44   ` Jakub Narebski
  2011-04-12 13:24     ` [PATCHv2 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
  2011-04-12 13:25     ` [PATCHv2 11/11] gitweb: Make JavaScript ability to adjust timezones configurable Jakub Narebski
  0 siblings, 2 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-12 12:44 UTC (permalink / raw)
  To: Kevin Cernekee; +Cc: git, John 'Warthog9' Hawley

On Tue, 12 Apr 2011, Kevin Cernekee wrote:
> On Sat, Apr 9, 2011 at 3:49 PM, Jakub Narebski <jnareb@gmail.com> wrote:
> >
> > This is split version (with assorted cleanups) of J.H. patch adding
> > JavaScript-base ability to change timezone in which dates are
> > displayed.
> 
> Jakub,
> 
> Thanks for the update.  This UI does seem to work better than the
> original "[+]" dropdown interface.

Thanks.

Notice that you can have only one timezone selection menu opened.  You
have to close existing to be able to open it in different place (near
different date), even though onclick handler still triggers.

This is caused by the fact that appending DocumentFragment to an element
"empties" it, as described in DOM 2 Core specification[1]:

  appendChild
    Adds the node newChild to the end of the list of children of this
    node. If the newChild is already in the tree, it is first removed.

    Parameters

       newChild of type Node
          The node to add.

          If it is a DocumentFragment object, the entire contents of the
          document fragment are moved into the child list of this node.
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[1]: http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-184E7107

That is also the case that we need to read it when closing timezone
selection menu in removeChangeTZForm():

	// timezone selection UI was appended as last child
	// see also displayChangeTZForm function
	var removed = container.removeChild(popup);
	if (documentFragment.firstChild !== removed) { // the only child
		// re-append it so it would be available for next time
		documentFragment.appendChild(removed);
	}

Though it wasn't what I thought it is needed for when writing i ;-)


You wrote in your response to original J.H. patch

  "Re: [PATCH 1/1] gitweb: javascript ability to adjust time based on timezone"
  http://thread.gmane.org/gmane.comp.version-control.git/169384/focus=169891

KC> I'm wondering if there might be a better place on the page to put the
KC> TZ selection.  It isn't immediately obvious to the user what the
KC> extra " + " does, and it seems to cause some issues.

Well, I am not sure if it is immediately obvious to user that he/she
can click on date to change its timezone.

Underlining on hover (like for links... though I guess that we really
should make style similar to links but not the same: it is not true link),
changing cursor and title attribute hopefully help here.

Ideas on having it better discoverable certainly welcome.

KC> If you decide to keep it where it is, you might want to consider
KC> absolute or fixed positioning so that other elements do not wrap
KC> around it.  IOW it would work more like the dropdown menus on many
KC> sites.

In this version it is done using absolute positioning, though there
are few quirks and things to know about:

 * To be able to position timezone menu relative to some element using
   absolute positioning you need to make said element relatively 
   positioned.

   	target.style.position = 'relative';

   We might have used CSS instead...

 * Floating inside block element works if it has some specified size.
   Normal, relative and fixed positioned elements have size given by
   its parent element.  For absolutely positioned block element you have
   to specify width explicitly, and not have it fit to width.

   That is why close-button uses absolute positioning rather than being
   a float.

 * The only absolute position relative to parent inline-block relative
   positioned element is top left.  All others doesn't work correctly
   e.g. in my ancient Mozilla 1.17.2 -- in 'log' view timezone menu got
   created on the right and outside viewport (page).

   That is why current solution is to use "top: 0; left: 0;".


KC> The timezone fixup javascript seemed to work reasonably well, except
KC> for the hiccup with IE6.  Maybe it would be worth splitting this into
KC> two patches: one to rewrite the timestamps, and a second one to add
KC> the TZ selection interface.

Well, I have split in more than 2 patches, but rewriting timezone of
timestamps is separate from TZ selection interface.

 
> The new code appears to degrade gracefully on IE6 (everything shown in UTC).

I don't know about IE6, but there was bug in IE-specific code in 
addCssRule (was 'stylesheets' when variable is named 'stylesheet').
Thanks for noticing this; fixed in new iteration.

Now at least in IE8 (Internet Explorer 8.0.6001.18702) it works as 
expected.
 
> Tests on Firefox 3.6.15 looked OK.

Tests in the following browsers looked OK:
 * Firefox 1.7.12 (Gecko/20050123)
 * Epiphany 1.6.5 (Gecko/20050123)
 * Firefox 4.0   - note: not as extensively tested
 * Firefox 3.6.16 (Gecko/20110319)
 * Internet Explorer 8.0 (8.0.6001.18702)


Tests in Konqueror 3.5.3 (KHTML) shown something strange: it has no
problems with named function expressions here

	document.onclick = function onclickHandler(event) {

but was showing syntax error in similar

	return function closeTZForm(event) {

that I used in new version of 10/11 patch.  As we don't redeploy handler,
we don't need function name, so in current version we use

	return function (event) {


Also after displaying timezone selection menu, and either selecting
timezone or canceling selection, so that timezone UI vanishes, the
date that was clicked ends up displaced a bit - moved slightly up.
This doesn't affect layout too badly, and is probably bug in this version
of Konqueror.

Tests in the following browsers gracefully degrade to UTC:
 * Lynx 2.8.5rel.1
 * ELinks 0.10.3
 * w3m 0.5.1+cvs-1.946

> 
> Chromium 6.0.472.62 (59676) does not like this operation:
> 
> Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
>   removeChangeTZForm
>   /gitweb-static/gitweb.js:785
> onTZFormChange
> 
> line 785: var removed = container.removeChild(container.lastChild);

Strange.  I confirm the same behavior in Google Chrome 10.0.648.204,
even after changes that I though should eliminate this exception.

Note that this bug/exception doesn't cause timezones to be not
translated, it only makes timezone selection menu not working.
 
removeChild() is DOM 2 Core method, `container.lastChild' exists and
is child of `container' element.  IE8, Firefox 3.6 and 4.0, Opera 10.63
all work, it's only Chrome / Chromium that has problems...

> Opera 10.63 resets target.selectedIndex to 1 after calling
> removeChangeTZForm() from onTZFormChange().  Net effect is that you're
> always stuck in the "local" zone.  Here is a workaround that fixed it
> for me:
> 
>        var target = event.target || event.srcElement;
>        var selectedIndex = target.selectedIndex;
> 
>        removeChangeTZForm(documentFragment, target, tzClassName);
> 
>        var selected = target.options.item(selectedIndex);

Or just change order of those statements:

	 event = event || window.event;
	 var target = event.target || event.srcElement;

	 var selected = target.options.item(target.selectedIndex);
	 removeChangeTZForm(tzSelectFragment, target, tzClassName);

Could you check that this version works correctly in Opera 10?
Thanks in advance.

> 
> Couple other random nitpicks:
> 
> > +	// server-side of gitweb produces datetime in UTC,
> > +	// so if tz is 'utc' there is no need for changes
> > +	var nochange = tz === 'utc';
> 
> If I delete my gitweb_tz cookie, then refresh the page:
> 
>  - All times show up in my browser's local time (good)
> 
>  - Clicking UTC/GMT on the dropdown has no immediate effect; I need to
>    refresh the page again to see the times change (bad)

I'm sorry, it looks like I tried to be too clever.

We should either do this _only_ in onloadTZSetup and not in fixDatetimeTZ,
or abandon this micro-optimization entirely - we have to enumerate all
elements with "datetime" class to add 'title' attribute anyway.

The first solution was chosen in next version of this patch.
 
> > +	my (undef, undef, $datetime_class) =
> > +		gitweb_get_feature('javascript-timezone');
> > +	if ($datetime_class) {
> > +		$strtime = qq!<span class="datetime">$strtime</span>!;
> 
> Should this hard-code class "datetime", or use $datetime_class from
> the gitweb server configuration?

Sorry, my mistake.  Fixed.

> > +/* JavaScript-base timezone manipulation */
> 
> Might want to reword as "JavaScript-based"

Done.
 
> > +		// refresh cookie, so it expires 7 days from last use of gitweb
> > +		setCookie(tzCookieName, tzCookie, { expires: 7, path: '/' });
> 
> Hmm, only 7 days?

Well, in new version where expire time is set in one place (was one to
set cookie after timezone selection, one to refresh cookie), and is 
increased to 14 days (2 week).  Please also remember that it is time
from last visit to gitweb, not last timezone change.

> > + * and instals appropriate onclick trigger (via event delegation).
> 
> "installs"

Done.


Thanks again for reviewing it.

-- 
Jakub Narebski
Poland

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCHv2 10/11] gitweb.js: Add UI for selecting common timezone to display dates
  2011-04-12 12:44   ` Jakub Narebski
@ 2011-04-12 13:24     ` Jakub Narebski
  2011-04-12 13:25     ` [PATCHv2 11/11] gitweb: Make JavaScript ability to adjust timezones configurable Jakub Narebski
  1 sibling, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-12 13:24 UTC (permalink / raw)
  To: Kevin Cernekee; +Cc: git, John 'Warthog9' Hawley

From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>

This will modify HTML, add CSS rules and add DOM event handlers so
that clicking on any date (the common part, not the localtime part)
will display a drop down menu to choose the timezone to change to.

Currently menu displays only the following timezones:

  utc
  local
  -1200
  -1100
  ...
  +1100
  +1200
  +1300
  +1400

In timezone selection menu each timezone is +1hr to the previous.  The
code is capable of handling fractional timezones, but those have not
been added to the menu.

All changes are saved to a cookie, so page changes and closing /
reopening browser retains the last known timezone setting used.

[jn: Changed from innerHTML to DOM, moved to event delegation for
onclick to trigger menu, added close button and cookie refreshing]

Signed-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Bugs-reported-by: Kevin Cernekee <cernekee@gmail.com>

Differences from v1:
* Fixed bug in gitweb.css (only /* ... */ are comments).
* fixDatetimeTZ didn't update timezone correctly if cookie was
  deleted and 'utc' timezone selected from menu
* Fix addCssRule for IE (typofix)
* Change code so that the fact that Opera 10.63 resets
  target.selectedIndex to 1 after calling removeChangeTZForm()
  doesn't affect timezone selection result.

* Pass all cookie parameters from gitweb.perl
* Increase cookie expiration time from 7 to 14 days
* Extract/refactor generateTZOptions function 
  (this makes createChangeTZForm code shorter)
* select.onchange, select.onblur, close-button.onclick handlers
  are now generated (this makes createChangeTZForm code shorter)
* removeChangeTZForm is more robust wrt future changes
* moved from top+right to top+left positioning, because it works
  correctly in wider range of [buggy] web browsers

 gitweb/gitweb.perl                  |    3 +-
 gitweb/static/gitweb.css            |   33 ++++
 gitweb/static/js/adjust-timezone.js |  294 +++++++++++++++++++++++++++++++++--
 gitweb/static/js/lib/common-lib.js  |   27 +++-
 4 files changed, 342 insertions(+), 15 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 6651946..b1e80ef 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3738,7 +3738,8 @@ sub git_footer_html {
 		      (gitweb_check_feature('javascript-actions') ?
 		      qq!	fixLinks();\n! : '').
 		      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html
-		      qq!	onloadTZSetup('local', 'gitweb_tz', 'datetime');\n!.
+		      qq!	var tz_cookie = { name: 'gitweb_tz', expires: 14, path: '/' };\n!. # in days
+		      qq!	onloadTZSetup('local', tz_cookie, 'datetime');\n!.
 		      qq!};\n!.
 		      qq!</script>\n!;
 	}
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index 79d7eeb..8dd0935 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -579,6 +579,39 @@ div.remote {
 	display: inline-block;
 }
 
+/* JavaScript-based timezone manipulation */
+
+.popup { /* timezone selection UI */
+	position: absolute;
+	/* "top: 0; right: 0;" would be better, if not for bugs in browsers */
+	top: 0; left: 0;
+	border: 1px solid;
+	padding: 2px;
+	background-color: #f0f0f0;
+	font-style: normal;
+	color: #000000;
+	cursor: auto;
+}
+
+.close-button { /* close timezone selection UI without selecting */
+	/* float doesn't work within absolutely positioned container,
+	 * if width of container is not set explicitly */
+	/* float: right; */
+	position: absolute;
+	top: 0px; right: 0px;
+	border:  1px solid green;
+	margin:  1px 1px 1px 1px;
+	padding-bottom: 2px;
+	width:     12px;
+	height:    10px;
+	font-size:  9px;
+	font-weight: bold;
+	text-align: center;
+	background-color: #fff0f0;
+	cursor: pointer;
+}
+
+
 /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
 
 /* Highlighting theme definition: */
diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js
index c9b69c3..f1e97a6 100644
--- a/gitweb/static/js/adjust-timezone.js
+++ b/gitweb/static/js/adjust-timezone.js
@@ -7,34 +7,51 @@
  */
 
 /**
- * Get common timezone and adjust dates to use this common timezone.
+ * 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 {String} tzCookieName: name of cookie to store timezone
+ * @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, tzCookieName, tzClassName) {
-	var tzCookie = getCookie(tzCookieName);
-	var tz = tzCookie ? tzCookie : tzDefault;
+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
-	if (tz !== 'utc') {
-		fixDatetimeTZ(tz, tzClassName);
-	}
+	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) {
+function fixDatetimeTZ(tz, tzClassName, nochange) {
 	// sanity check, method should be ensured by common-lib.js
 	if (!document.getElementsByClassName) {
 		return;
@@ -48,12 +65,263 @@ function fixDatetimeTZ(tz, tzClassName) {
 	for (var i = 0, len = classesFound.length; i < len; i++) {
 		var curElement = classesFound[i];
 
-		var epoch = parseRFC2822Date(curElement.innerHTML);
-		var adjusted = formatDateRFC2882(epoch, tz);
+		curElement.title = 'Click to change timezone';
+		if (!nochange) {
+			var epoch = parseRFC2822Date(curElement.innerHTML);
+			var adjusted = formatDateRFC2882(epoch, tz);
+
+			// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2
+			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:
+			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);
+	// NOTE: onblur removal might be not necessary with close button
+	select.onblur = closeTZFormHandler(documentFragment, 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;
+	}
 
-		// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2
-		curElement.firstChild.data = adjusted;
+	// timezone selection UI was appended as last child
+	// see also displayChangeTZForm function
+	var removed = container.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
+	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&plusmn;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 */
diff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js
index b371391..018bbb7 100644
--- a/gitweb/static/js/lib/common-lib.js
+++ b/gitweb/static/js/lib/common-lib.js
@@ -64,7 +64,7 @@ function padLeft(input, width, ch) {
 
 
 /* ............................................................ */
-/* Ajax */
+/* Handling browser incompatibilities */
 
 /**
  * Create XMLHttpRequest object in cross-browser way
@@ -88,6 +88,31 @@ function createRequestObject() {
 }
 
 
+/**
+ * Insert rule giving specified STYLE to given SELECTOR at the end of
+ * first CSS stylesheet.
+ *
+ * @param {String} selector: CSS selector, e.g. '.class'
+ * @param {String} style: rule contents, e.g. 'background-color: red;'
+ */
+function addCssRule(selector, style) {
+	var stylesheet = document.styleSheets[0];
+
+	var theRules = [];
+	if (stylesheet.cssRules) {     // W3C way
+		theRules = stylesheet.cssRules;
+	} else if (stylesheet.rules) { // IE way
+		theRules = stylesheet.rules;
+	}
+
+	if (stylesheet.insertRule) {    // W3C way
+		stylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length);
+	} else if (stylesheet.addRule) { // IE way
+		stylesheet.addRule(selector, style);
+	}
+}
+
+
 /* ............................................................ */
 /* Support for legacy browsers */
 
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCHv2 11/11] gitweb: Make JavaScript ability to adjust timezones configurable
  2011-04-12 12:44   ` Jakub Narebski
  2011-04-12 13:24     ` [PATCHv2 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
@ 2011-04-12 13:25     ` Jakub Narebski
  1 sibling, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-12 13:25 UTC (permalink / raw)
  To: Kevin Cernekee; +Cc: git, John 'Warthog9' Hawley

Configure JavaScript-based ability to select common timezone for git
dates via %feature mechanism, namely 'javascript-timezone' feature.

The following settings are configurable:
* default timezone (defaults to 'local' i.e. browser timezone);
  this also can function as a way to disable this ability,
  by setting it to false-ish value (undef or '')
* name of cookie to store user's choice of timezone
* class name to mark dates


NOTE: This is a bit of abuse of %feature system, which can store only
sequence of values, rather than dictionary (hash); usually but not
always only a single value is used.

Based-on-code-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Bugs-reported-by: Kevin Cernekee <cernekee@gmail.com>

Changes from v1:
* Fixed bug in format_timestamp_html using "datetime" instead
  of $datetime_class.

 gitweb/gitweb.perl |   39 +++++++++++++++++++++++++++++++--------
 1 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index b1e80ef..ac335b6 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -480,6 +480,18 @@ our %feature = (
 		'override' => 0,
 		'default' => [0]},
 
+	# Enable and configure ability to change common timezone for dates
+	# in gitweb output via JavaScript.  Enabled by default.
+	# Project specific override is not supported.
+	'javascript-timezone' => {
+		'override' => 0,
+		'default' => [
+			'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,
+			             # or undef to turn off this feature
+			'gitweb_tz', # name of cookie where to store selected timezone
+			'datetime',  # CSS class used to mark up dates for manipulation
+		]},
+
 	# Syntax highlighting support. This is based on Daniel Svensson's
 	# and Sham Chukoury's work in gitweb-xmms2.git.
 	# It requires the 'highlight' program present in $PATH,
@@ -3733,14 +3745,19 @@ sub git_footer_html {
 		      qq!           "!. href() .qq!");\n!.
 		      qq!</script>\n!;
 	} else {
+		my ($jstimezone, $tz_cookie, $datetime_class) =
+			gitweb_get_feature('javascript-timezone');
+
 		print qq!<script type="text/javascript">\n!.
-		      qq!window.onload = function () {\n!.
-		      (gitweb_check_feature('javascript-actions') ?
-		      qq!	fixLinks();\n! : '').
-		      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html
-		      qq!	var tz_cookie = { name: 'gitweb_tz', expires: 14, path: '/' };\n!. # in days
-		      qq!	onloadTZSetup('local', tz_cookie, 'datetime');\n!.
-		      qq!};\n!.
+		      qq!window.onload = function () {\n!;
+		if (gitweb_check_feature('javascript-actions')) {
+			print qq!	fixLinks();\n!;
+		}
+		if ($jstimezone && $tz_cookie && $datetime_class) {
+			print qq!	var tz_cookie = { name: '$tz_cookie', expires: 14, path: '/' };\n!. # in days
+			      qq!	onloadTZSetup('$jstimezone', tz_cookie, '$datetime_class');\n!;
+		}
+		print qq!};\n!.
 		      qq!</script>\n!;
 	}
 
@@ -3946,7 +3963,13 @@ sub git_print_section {
 
 sub format_timestamp_html {
 	my $date = shift;
-	my $strtime = '<span class="datetime">'.$date->{'rfc2822'}.'</span>';
+	my $strtime = $date->{'rfc2822'};
+
+	my (undef, undef, $datetime_class) =
+		gitweb_get_feature('javascript-timezone');
+	if ($datetime_class) {
+		$strtime = qq!<span class="$datetime_class">$strtime</span>!;
+	}
 
 	my $localtime_format = '(%02d:%02d %s)';
 	if ($date->{'hour_local'} < 6) {
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone
  2011-04-15 14:43 [PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript Jakub Narebski
@ 2011-04-15 14:44 ` Jakub Narebski
  0 siblings, 0 replies; 20+ messages in thread
From: Jakub Narebski @ 2011-04-15 14:44 UTC (permalink / raw)
  To: git; +Cc: John 'Warthog9' Hawley, Kevin Cernekee, Jakub Narebski

From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>

This patch is based on Kevin Cernekee's <cernekee@gmail.com>
patch series entitled "gitweb: introduce localtime feature".  While
Kevin's patch changed the server side output so that the timezone
was output from gitweb itself, this has a number of drawbacks, in
particular with respect to gitweb-caching.

This patch takes the same basic goal, display the appropriate times in
a given common timezone, and implements it in JavaScript.  This
requires adding / using a new class, "datetime", to be able to find
elements to be adjusted from JavaScript.  Appropriate dates are
wrapped in a span with this class.

Timezone to be used can be retrieved from "gitweb_tz" cookie, though
currently there is no way to set / manipulate this cookie from gitweb;
this is left for later commit.

Valid timezones, currently, are: "utc", "local" (which means that
timezone is taken from browser), and "+/-ZZZZ" numeric timezone as in
RFC-2822.  Default timezone is "local" (currently not configurable,
left for later commit).

Fallback (should JavaScript not be enabled) is to treat dates as they
have been and display them, only, in UTC.

Pages affected:
* 'summary' view, "last change" field (commit time from latest change)
* 'log' view, author time
* 'commit' and 'commitdiff' views, author/committer time
* 'tag' view, tagger time

Based-on-code-from: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
The only change from v1 is fixing typo 'utf' -> 'utc' timezone.

 gitweb/Makefile                     |    1 +
 gitweb/gitweb.perl                  |   11 +++++--
 gitweb/static/js/adjust-timezone.js |   59 +++++++++++++++++++++++++++++++++++
 gitweb/static/js/lib/datetime.js    |   15 +++++++++
 4 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 gitweb/static/js/adjust-timezone.js

diff --git a/gitweb/Makefile b/gitweb/Makefile
index 7dd1dee..5d20515 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -120,6 +120,7 @@ GITWEB_JSLIB_FILES += static/js/lib/common-lib.js
 GITWEB_JSLIB_FILES += static/js/lib/datetime.js
 GITWEB_JSLIB_FILES += static/js/lib/cookies.js
 GITWEB_JSLIB_FILES += static/js/javascript-detection.js
+GITWEB_JSLIB_FILES += static/js/adjust-timezone.js
 GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 
 
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 67bcfe8..6651946 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3732,9 +3732,14 @@ sub git_footer_html {
 		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
 		      qq!           "!. href() .qq!");\n!.
 		      qq!</script>\n!;
-	} elsif (gitweb_check_feature('javascript-actions')) {
+	} else {
 		print qq!<script type="text/javascript">\n!.
-		      qq!window.onload = fixLinks;\n!.
+		      qq!window.onload = function () {\n!.
+		      (gitweb_check_feature('javascript-actions') ?
+		      qq!	fixLinks();\n! : '').
+		      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html
+		      qq!	onloadTZSetup('local', 'gitweb_tz', 'datetime');\n!.
+		      qq!};\n!.
 		      qq!</script>\n!;
 	}
 
@@ -3940,7 +3945,7 @@ sub git_print_section {
 
 sub format_timestamp_html {
 	my $date = shift;
-	my $strtime = $date->{'rfc2822'};
+	my $strtime = '<span class="datetime">'.$date->{'rfc2822'}.'</span>';
 
 	my $localtime_format = '(%02d:%02d %s)';
 	if ($date->{'hour_local'} < 6) {
diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js
new file mode 100644
index 0000000..c9b69c3
--- /dev/null
+++ b/gitweb/static/js/adjust-timezone.js
@@ -0,0 +1,59 @@
+// 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 and adjust dates to use this common timezone.
+ *
+ * This function is called during onload event (added to window.onload).
+ *
+ * @param {String} tzDefault: default timezone, if there is no cookie
+ * @param {String} tzCookieName: name of cookie to store timezone
+ * @param {String} tzClassName: denotes elements with date to be adjusted
+ */
+function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {
+	var tzCookie = getCookie(tzCookieName);
+	var tz = tzCookie ? tzCookie : tzDefault;
+
+	// server-side of gitweb produces datetime in UTC,
+	// so if tz is 'utc' there is no need for changes
+	if (tz !== 'utc') {
+		fixDatetimeTZ(tz, tzClassName);
+	}
+}
+
+
+/**
+ * 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
+ */
+function fixDatetimeTZ(tz, tzClassName) {
+	// 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];
+
+		var epoch = parseRFC2822Date(curElement.innerHTML);
+		var adjusted = formatDateRFC2882(epoch, tz);
+
+		// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2
+		curElement.firstChild.data = adjusted;
+	}
+}
+
+/* end of adjust-timezone.js */
diff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js
index b3dcedb..f78c60a 100644
--- a/gitweb/static/js/lib/datetime.js
+++ b/gitweb/static/js/lib/datetime.js
@@ -105,6 +105,21 @@ function formatTimezoneInfo(hours, minutes, sep) {
 }
 
 /**
+ * translate 'utc' and 'local' to numerical timezone
+ * @param {String} timezoneInfo: might be 'utc' or 'local' (browser)
+ */
+function normalizeTimezoneInfo(timezoneInfo) {
+	switch (timezoneInfo) {
+	case 'utc':
+		return '+0000';
+	case 'local': // 'local' is browser timezone
+		return localTimezoneInfo();
+	}
+	return timezoneInfo;
+}
+
+
+/**
  * return date in local time formatted in iso-8601 like format
  * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'
  *
-- 
1.7.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2011-04-15 14:44 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-09 22:49 [PATCH 00/11] gitweb: Change timezone Jakub Narebski
2011-04-09 22:49 ` [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build Jakub Narebski
2011-04-09 22:49 ` [PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files Jakub Narebski
2011-04-09 22:49 ` [PATCH/RFC 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft Jakub Narebski
2011-04-09 22:49 ` [PATCH 04/11] gitweb.js: Extract and improve datetime handling Jakub Narebski
2011-04-09 22:49 ` [PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js Jakub Narebski
2011-04-09 22:49 ` [PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists) Jakub Narebski
2011-04-09 22:49 ` [PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html Jakub Narebski
2011-04-09 22:49 ` [RFC/PATCH 08/11] gitweb: Unify the way long timestamp is displayed Jakub Narebski
2011-04-09 22:49 ` [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone Jakub Narebski
2011-04-09 22:49 ` [PATCH/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
2011-04-10  9:36   ` Jakub Narebski
2011-04-10 14:39     ` Jakub Narebski
2011-04-10 15:10   ` Jakub Narebski
2011-04-09 22:49 ` [PATCH/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable Jakub Narebski
2011-04-12  1:19 ` [PATCH 00/11] gitweb: Change timezone Kevin Cernekee
2011-04-12 12:44   ` Jakub Narebski
2011-04-12 13:24     ` [PATCHv2 10/11] gitweb.js: Add UI for selecting common timezone to display dates Jakub Narebski
2011-04-12 13:25     ` [PATCHv2 11/11] gitweb: Make JavaScript ability to adjust timezones configurable Jakub Narebski
  -- strict thread matches above, loose matches on Subject: below --
2011-04-15 14:43 [PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript Jakub Narebski
2011-04-15 14:44 ` [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone Jakub Narebski

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).