{"thread":{"id":"27104","subject":"[PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript","startedAt":"2011-04-15T14:43:54Z","lastAt":"2011-04-19T17:18:25Z","messageCount":21,"participants":["Jakub Narebski","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"165891","messageId":"1302878645-458-1-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":null,"subject":"[PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:43:54Z","receivedAt":"2011-04-15T14:43:54Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"This is second version of this series.  First version was sent as\n\n  [PATCH 00/11] gitweb: Change timezone\n  http://thread.gmane.org/gmane.comp.version-control.git/171212\n\nThe major issue left is that changing (selecting) timezone doesn't\nwork on Chrome / Chromium, as reported by Kevin Cernekee.  See patch\n10/11 for more details.\n\nNOTE that this time, in this version of series, the path-by-patch\ncomparison with original J.H. patches is not provided.  It would be\nre-added in the final version.\n\n\nThis is split version (with assorted cleanups) of J.H. patch adding\nJavaScript-base ability to change timezone in which dates are\ndisplayed.\n\n  [PATCH 0/1] Gitweb: Change timezone\n    [PATCH 1/1] gitweb: javascript ability to adjust time based on timezone\n  Message-Id: <1300925335-3212-1-git-send-email-warthog9@eaglescrag.net>\n  http://thread.gmane.org/gmane.comp.version-control.git/169384/focus=169881\n\nBelow there is copy of original J.H. announcement:\nJH>\nJH> This is just a javascript implementation of Kevin's localtime\nJH> feature.  It's pretty straight forward, though date handling in\nJH> Javascript is, special (head bangingly so).\nJH> \nJH> This should be good to run on any browser, with the safe fallback\nJH> of UTC being the default output should Javascript not work / be\nJH> available.\n\nTable of contents:\n~~~~~~~~~~~~~~~~~~\n* [PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build\n  [PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files\n  [PATCH 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft\n  [PATCH 04/11] gitweb.js: Extract and improve datetime handling\n  [PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js\n  [PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists)\n  [PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html\n  [PATCH 08/11] gitweb: Unify the way long timestamp is displayed\n\n  Unchanged from previous version of this series.\n\n* [PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone\n  \n  Cosmetic changes compared to previous version of this patch.\n\n* [PATCHv2/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates\n  [PATCHv2/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable\n\n  The main part.  The major part of changes is fixing noted issues,\n  and bugs reported by Kevin Cernekee in response to v1 of series.\n\n  The first patch of two, 10/11, includes additionally some\n  refactorings that should make code cleaner and easier to follow\n  (reducing size of functions).\n\n  This is the same or nearly the same version of those two patches as\n  sent to git mailing list as:\n\n    http://thread.gmane.org/gmane.comp.version-control.git/171212/focus=171389\n\n\nShortlog:\n~~~~~~~~~\nJakub Narebski (9):\n  gitweb: Split JavaScript for maintability, combining on build\n  gitweb.js: Update and improve comments in JavaScript files\n  gitweb.js: Provide default values for padding in padLeftStr and padLeft\n  gitweb.js: Extract and improve datetime handling\n  gitweb.js: Introduce gitweb/static/js/lib/cookies.js\n  gitweb.js: Provide getElementsByClassName method (if it not exists)\n  gitweb: Refactor generating of long dates into format_timestamp_html\n  gitweb: Unify the way long timestamp is displayed\n  gitweb: Make JavaScript ability to adjust timezones configurable\n\nJohn 'Warthog9' Hawley (2):\n  gitweb: JavaScript ability to adjust time based on timezone\n  gitweb.js: Add UI for selecting common timezone to display dates\n\nDiffstat:\n~~~~~~~~~\n .gitignore                                         |    1 +\n gitweb/Makefile                                    |   19 +-\n gitweb/gitweb.perl                                 |   76 +++--\n gitweb/static/gitweb.css                           |   33 ++\n gitweb/static/js/README                            |   20 ++\n gitweb/static/js/adjust-timezone.js                |  330 ++++++++++++++++++++\n .../static/{gitweb.js => js/blame_incremental.js}  |  228 +-------------\n gitweb/static/js/javascript-detection.js           |   43 +++\n gitweb/static/js/lib/common-lib.js                 |  224 +++++++++++++\n gitweb/static/js/lib/cookies.js                    |  114 +++++++\n gitweb/static/js/lib/datetime.js                   |  176 +++++++++++\n 11 files changed, 1023 insertions(+), 241 deletions(-)\n create mode 100644 gitweb/static/js/README\n create mode 100644 gitweb/static/js/adjust-timezone.js\n rename gitweb/static/{gitweb.js => js/blame_incremental.js} (74%)\n create mode 100644 gitweb/static/js/javascript-detection.js\n create mode 100644 gitweb/static/js/lib/common-lib.js\n create mode 100644 gitweb/static/js/lib/cookies.js\n create mode 100644 gitweb/static/js/lib/datetime.js\n\nDirstat:\n~~~~~~~~\n  40.1% gitweb/static/js/lib/\n  32.7% gitweb/static/js/\n  18.3% gitweb/static/\n   8.7% gitweb/\n-- \n1.7.3\n"},{"id":"165898","messageId":"1302878645-458-2-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 01/11] gitweb: Split JavaScript for maintability, combining on build","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:43:55Z","receivedAt":"2011-04-15T14:43:55Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Split originally single gitweb.js file into smaller files, each\ndealing with single issue / area of responsibility.  This move should\nmake gitweb's JavaScript code easier to maintain.\n\nFor better webapp performance it is recommended[1][2][3] to combine\nJavaScript files.  Do it during build time (in gitweb/Makefile), by\nstraight concatenation of files into gitweb.js file (which is now\nignored as being generated).  This means that there are no changes to\ngitweb script itself - it still uses gitweb.js or gitweb.min.js, but\nnow generated.\n\n[1]: http://developer.yahoo.com/performance/rules.html\n     \"Minimize HTTP Requests\" section\n[2]: http://code.google.com/speed/articles/include-scripts-properly.html\n     \"1. Combine external JavaScript files\"\n[3]: http://javascript-reference.info/speed-up-your-javascript-load-time.htm\n     \"Combine Your Files\" section.\n\nSee also new gitweb/static/js/README file.\n\nInspired-by-patch-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nUnchanged from v1 version.\n\n\nWhat is unfortunately not visible from diffstat below is that it is\nalmost straightforward split of original gitweb/statis/gitweb.js file,\nwith only some header and footer comments added.\n\nIs there any magic git-diff / git-show invocation that is able to turn\ndiff describing splitting a file into patch involving solely renames /\ncopies in split result?\n\n .gitignore                                         |    1 +\n gitweb/Makefile                                    |   16 ++-\n gitweb/static/js/README                            |   20 ++\n .../static/{gitweb.js => js/blame_incremental.js}  |  208 +-------------------\n gitweb/static/js/javascript-detection.js           |   43 ++++\n gitweb/static/js/lib/common-lib.js                 |  187 ++++++++++++++++++\n 6 files changed, 269 insertions(+), 206 deletions(-)\n create mode 100644 gitweb/static/js/README\n rename gitweb/static/{gitweb.js => js/blame_incremental.js} (76%)\n create mode 100644 gitweb/static/js/javascript-detection.js\n create mode 100644 gitweb/static/js/lib/common-lib.js\n\ndiff --git a/.gitignore b/.gitignore\nindex 2cf3ca5..9e95005 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -158,6 +158,7 @@\n /gitk-git/gitk-wish\n /gitweb/GITWEB-BUILD-OPTIONS\n /gitweb/gitweb.cgi\n+/gitweb/static/gitweb.js\n /gitweb/static/gitweb.min.*\n /test-chmtime\n /test-ctype\ndiff --git a/gitweb/Makefile b/gitweb/Makefile\nindex 0a6ac00..0baa9df 100644\n--- a/gitweb/Makefile\n+++ b/gitweb/Makefile\n@@ -86,7 +86,7 @@ ifndef V\n endif\n endif\n \n-all:: gitweb.cgi\n+all:: gitweb.cgi static/gitweb.js\n \n GITWEB_PROGRAMS = gitweb.cgi\n \n@@ -112,6 +112,15 @@ endif\n \n GITWEB_FILES += static/git-logo.png static/git-favicon.png\n \n+# JavaScript files that are composed (concatenated) to form gitweb.js\n+#\n+# js/lib/common-lib.js should be always first, then js/lib/*.js,\n+# then the rest of files; js/gitweb.js should be last (if it exists)\n+GITWEB_JSLIB_FILES += static/js/lib/common-lib.js\n+GITWEB_JSLIB_FILES += static/js/javascript-detection.js\n+GITWEB_JSLIB_FILES += static/js/blame_incremental.js\n+\n+\n GITWEB_REPLACE = \\\n \t-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \\\n \t-e 's|++GIT_BINDIR++|$(bindir)|g' \\\n@@ -146,6 +155,11 @@ gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS\n \tchmod +x $@+ && \\\n \tmv $@+ $@\n \n+static/gitweb.js: $(GITWEB_JSLIB_FILES)\n+\t$(QUIET_GEN)$(RM) $@ $@+ && \\\n+\tcat $^ >$@+ && \\\n+\tmv $@+ $@\n+\n ### Testing rules\n \n test:\ndiff --git a/gitweb/static/js/README b/gitweb/static/js/README\nnew file mode 100644\nindex 0000000..f8460ed\n--- /dev/null\n+++ b/gitweb/static/js/README\n@@ -0,0 +1,20 @@\n+GIT web interface (gitweb) - JavaScript\n+=======================================\n+\n+This directory holds JavaScript code used by gitweb (GIT web interface).\n+Scripts from there would be concatenated together in the order specified\n+by gitweb/Makefile into gitweb/static/gitweb.js, during building of\n+gitweb/gitweb.cgi (during gitweb building).  The resulting file (or its\n+minification) would then be installed / deployed together with gitweb.\n+\n+Scripts in 'lib/' subdirectory compose generic JavaScript library,\n+providing features required by gitweb but in no way limited to gitweb\n+only.  In the future those scripts could be replaced by some JavaScript\n+library / framework, like e.g. jQuery, YUI, Prototype, MooTools, Dojo,\n+ExtJS, Script.aculo.us or SproutCore.\n+\n+All scripts that manipulate gitweb output should be put outside 'lib/',\n+directly in this directory ('gitweb/static/js/').  Those scripts would\n+have to be rewritten if gitweb moves to using some JavaScript library.\n+\n+See also comments in gitweb/Makefile.\ndiff --git a/gitweb/static/gitweb.js b/gitweb/static/js/blame_incremental.js\nsimilarity index 76%\nrename from gitweb/static/gitweb.js\nrename to gitweb/static/js/blame_incremental.js\nindex 40ec084..f63f78b 100644\n--- a/gitweb/static/gitweb.js\n+++ b/gitweb/static/js/blame_incremental.js\n@@ -1,44 +1,12 @@\n // Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>\n //               2007, Petr Baudis <pasky@suse.cz>\n-//          2008-2009, Jakub Narebski <jnareb@gmail.com>\n+//          2008-2011, Jakub Narebski <jnareb@gmail.com>\n \n /**\n- * @fileOverview JavaScript code for gitweb (git web interface).\n+ * @fileOverview JavaScript side of Ajax-y 'blame_incremental' view in gitweb\n  * @license GPLv2 or later\n  */\n \n-/* ============================================================ */\n-/* functions for generic gitweb actions and views */\n-\n-/**\n- * used to check if link has 'js' query parameter already (at end),\n- * and other reasons to not add 'js=1' param at the end of link\n- * @constant\n- */\n-var jsExceptionsRe = /[;?]js=[01]$/;\n-\n-/**\n- * Add '?js=1' or ';js=1' to the end of every link in the document\n- * that doesn't have 'js' query parameter set already.\n- *\n- * Links with 'js=1' lead to JavaScript version of given action, if it\n- * exists (currently there is only 'blame_incremental' for 'blame')\n- *\n- * @globals jsExceptionsRe\n- */\n-function fixLinks() {\n-\tvar allLinks = document.getElementsByTagName(\"a\") || document.links;\n-\tfor (var i = 0, len = allLinks.length; i < len; i++) {\n-\t\tvar link = allLinks[i];\n-\t\tif (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;\n-\t\t\tlink.href +=\n-\t\t\t\t(link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';\n-\t\t}\n-\t}\n-}\n-\n-\n-/* ============================================================ */\n \n /*\n  * This code uses DOM methods instead of (nonstandard) innerHTML\n@@ -59,71 +27,6 @@ function fixLinks() {\n \n \n /* ============================================================ */\n-/* generic utility functions */\n-\n-\n-/**\n- * pad number N with nonbreakable spaces on the left, to WIDTH characters\n- * example: padLeftStr(12, 3, '\\u00A0') == '\\u00A012'\n- *          ('\\u00A0' is nonbreakable space)\n- *\n- * @param {Number|String} input: number to pad\n- * @param {Number} width: visible width of output\n- * @param {String} str: string to prefix to string, e.g. '\\u00A0'\n- * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR\n- */\n-function padLeftStr(input, width, str) {\n-\tvar prefix = '';\n-\n-\twidth -= input.toString().length;\n-\twhile (width > 0) {\n-\t\tprefix += str;\n-\t\twidth--;\n-\t}\n-\treturn prefix + input;\n-}\n-\n-/**\n- * Pad INPUT on the left to SIZE width, using given padding character CH,\n- * for example padLeft('a', 3, '_') is '__a'.\n- *\n- * @param {String} input: input value converted to string.\n- * @param {Number} width: desired length of output.\n- * @param {String} ch: single character to prefix to string.\n- *\n- * @returns {String} Modified string, at least SIZE length.\n- */\n-function padLeft(input, width, ch) {\n-\tvar s = input + \"\";\n-\twhile (s.length < width) {\n-\t\ts = ch + s;\n-\t}\n-\treturn s;\n-}\n-\n-/**\n- * Create XMLHttpRequest object in cross-browser way\n- * @returns XMLHttpRequest object, or null\n- */\n-function createRequestObject() {\n-\ttry {\n-\t\treturn new XMLHttpRequest();\n-\t} catch (e) {}\n-\ttry {\n-\t\treturn window.createRequest();\n-\t} catch (e) {}\n-\ttry {\n-\t\treturn new ActiveXObject(\"Msxml2.XMLHTTP\");\n-\t} catch (e) {}\n-\ttry {\n-\t\treturn new ActiveXObject(\"Microsoft.XMLHTTP\");\n-\t} catch (e) {}\n-\n-\treturn null;\n-}\n-\n-\n-/* ============================================================ */\n /* utility/helper functions (and variables) */\n \n var xhr;        // XMLHttpRequest object\n@@ -392,111 +295,6 @@ function fixColorsAndGroups() {\n \t}\n }\n \n-/* ............................................................ */\n-/* time and data */\n-\n-/**\n- * used to extract hours and minutes from timezone info, e.g '-0900'\n- * @constant\n- */\n-var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;\n-\n-/**\n- * convert numeric timezone +/-ZZZZ to offset from UTC in seconds\n- *\n- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n- * @returns {Number} offset from UTC in seconds for timezone\n- *\n- * @globals tzRe\n- */\n-function timezoneOffset(timezoneInfo) {\n-\tvar match = tzRe.exec(timezoneInfo);\n-\tvar tz_sign = (match[1] === '-' ? -1 : +1);\n-\tvar tz_hour = parseInt(match[2],10);\n-\tvar tz_min  = parseInt(match[3],10);\n-\n-\treturn tz_sign*(((tz_hour*60) + tz_min)*60);\n-}\n-\n-/**\n- * return date in local time formatted in iso-8601 like format\n- * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'\n- *\n- * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'\n- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n- * @returns {String} date in local time in iso-8601 like format\n- */\n-function formatDateISOLocal(epoch, timezoneInfo) {\n-\t// date corrected by timezone\n-\tvar localDate = new Date(1000 * (epoch +\n-\t\ttimezoneOffset(timezoneInfo)));\n-\tvar localDateStr = // e.g. '2005-08-07'\n-\t\tlocalDate.getUTCFullYear()                 + '-' +\n-\t\tpadLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +\n-\t\tpadLeft(localDate.getUTCDate(),    2, '0');\n-\tvar localTimeStr = // e.g. '21:49:46'\n-\t\tpadLeft(localDate.getUTCHours(),   2, '0') + ':' +\n-\t\tpadLeft(localDate.getUTCMinutes(), 2, '0') + ':' +\n-\t\tpadLeft(localDate.getUTCSeconds(), 2, '0');\n-\n-\treturn localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;\n-}\n-\n-/* ............................................................ */\n-/* unquoting/unescaping filenames */\n-\n-/**#@+\n- * @constant\n- */\n-var escCodeRe = /\\\\([^0-7]|[0-7]{1,3})/g;\n-var octEscRe = /^[0-7]{1,3}$/;\n-var maybeQuotedRe = /^\\\"(.*)\\\"$/;\n-/**#@-*/\n-\n-/**\n- * unquote maybe git-quoted filename\n- * e.g. 'aa' -> 'aa', '\"a\\ta\"' -> 'a\ta'\n- *\n- * @param {String} str: git-quoted string\n- * @returns {String} Unquoted and unescaped string\n- *\n- * @globals escCodeRe, octEscRe, maybeQuotedRe\n- */\n-function unquote(str) {\n-\tfunction unq(seq) {\n-\t\tvar es = {\n-\t\t\t// character escape codes, aka escape sequences (from C)\n-\t\t\t// replacements are to some extent JavaScript specific\n-\t\t\tt: \"\\t\",   // tab            (HT, TAB)\n-\t\t\tn: \"\\n\",   // newline        (NL)\n-\t\t\tr: \"\\r\",   // return         (CR)\n-\t\t\tf: \"\\f\",   // form feed      (FF)\n-\t\t\tb: \"\\b\",   // backspace      (BS)\n-\t\t\ta: \"\\x07\", // alarm (bell)   (BEL)\n-\t\t\te: \"\\x1B\", // escape         (ESC)\n-\t\t\tv: \"\\v\"    // vertical tab   (VT)\n-\t\t};\n-\n-\t\tif (seq.search(octEscRe) !== -1) {\n-\t\t\t// octal char sequence\n-\t\t\treturn String.fromCharCode(parseInt(seq, 8));\n-\t\t} else if (seq in es) {\n-\t\t\t// C escape sequence, aka character escape code\n-\t\t\treturn es[seq];\n-\t\t}\n-\t\t// quoted ordinary character\n-\t\treturn seq;\n-\t}\n-\n-\tvar match = str.match(maybeQuotedRe);\n-\tif (match) {\n-\t\tstr = match[1];\n-\t\t// perhaps str = eval('\"'+str+'\"'); would be enough?\n-\t\tstr = str.replace(escCodeRe,\n-\t\t\tfunction (substr, p1, offset, s) { return unq(p1); });\n-\t}\n-\treturn str;\n-}\n \n /* ============================================================ */\n /* main part: parsing response */\n@@ -886,4 +684,4 @@ function startBlame(blamedataUrl, bUrl) {\n \tpollTimer = setInterval(xhr.onreadystatechange, 1000);\n }\n \n-// end of gitweb.js\n+/* end of blame_incremental.js */\ndiff --git a/gitweb/static/js/javascript-detection.js b/gitweb/static/js/javascript-detection.js\nnew file mode 100644\nindex 0000000..93dd2bd\n--- /dev/null\n+++ b/gitweb/static/js/javascript-detection.js\n@@ -0,0 +1,43 @@\n+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>\n+//               2007, Petr Baudis <pasky@suse.cz>\n+//          2008-2011, Jakub Narebski <jnareb@gmail.com>\n+\n+/**\n+ * @fileOverview Detect if JavaScript is enabled, and pass it to server-side\n+ * @license GPLv2 or later\n+ */\n+\n+\n+/* ============================================================ */\n+/* Manipulating links */\n+\n+/**\n+ * used to check if link has 'js' query parameter already (at end),\n+ * and other reasons to not add 'js=1' param at the end of link\n+ * @constant\n+ */\n+var jsExceptionsRe = /[;?]js=[01]$/;\n+\n+/**\n+ * Add '?js=1' or ';js=1' to the end of every link in the document\n+ * that doesn't have 'js' query parameter set already.\n+ *\n+ * Links with 'js=1' lead to JavaScript version of given action, if it\n+ * exists (currently there is only 'blame_incremental' for 'blame')\n+ *\n+ * To be used as `window.onload` handler\n+ *\n+ * @globals jsExceptionsRe\n+ */\n+function fixLinks() {\n+\tvar allLinks = document.getElementsByTagName(\"a\") || document.links;\n+\tfor (var i = 0, len = allLinks.length; i < len; i++) {\n+\t\tvar link = allLinks[i];\n+\t\tif (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;\n+\t\t\tlink.href +=\n+\t\t\t\t(link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';\n+\t\t}\n+\t}\n+}\n+\n+/* end of javascript-detection.js */\ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nnew file mode 100644\nindex 0000000..38f3b9e\n--- /dev/null\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -0,0 +1,187 @@\n+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>\n+//               2007, Petr Baudis <pasky@suse.cz>\n+//          2008-2011, Jakub Narebski <jnareb@gmail.com>\n+\n+/**\n+ * @fileOverview Generic JavaScript code (helper functions)\n+ * @license GPLv2 or later\n+ */\n+\n+\n+/* ============================================================ */\n+/* ............................................................ */\n+/* Padding */\n+\n+/**\n+ * pad number N with nonbreakable spaces on the left, to WIDTH characters\n+ * example: padLeftStr(12, 3, '\\u00A0') == '\\u00A012'\n+ *          ('\\u00A0' is nonbreakable space)\n+ *\n+ * @param {Number|String} input: number to pad\n+ * @param {Number} width: visible width of output\n+ * @param {String} str: string to prefix to string, e.g. '\\u00A0'\n+ * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR\n+ */\n+function padLeftStr(input, width, str) {\n+\tvar prefix = '';\n+\n+\twidth -= input.toString().length;\n+\twhile (width > 0) {\n+\t\tprefix += str;\n+\t\twidth--;\n+\t}\n+\treturn prefix + input;\n+}\n+\n+/**\n+ * Pad INPUT on the left to SIZE width, using given padding character CH,\n+ * for example padLeft('a', 3, '_') is '__a'.\n+ *\n+ * @param {String} input: input value converted to string.\n+ * @param {Number} width: desired length of output.\n+ * @param {String} ch: single character to prefix to string.\n+ *\n+ * @returns {String} Modified string, at least SIZE length.\n+ */\n+function padLeft(input, width, ch) {\n+\tvar s = input + \"\";\n+\twhile (s.length < width) {\n+\t\ts = ch + s;\n+\t}\n+\treturn s;\n+}\n+\n+\n+/* ............................................................ */\n+/* Ajax */\n+\n+/**\n+ * Create XMLHttpRequest object in cross-browser way\n+ * @returns XMLHttpRequest object, or null\n+ */\n+function createRequestObject() {\n+\ttry {\n+\t\treturn new XMLHttpRequest();\n+\t} catch (e) {}\n+\ttry {\n+\t\treturn window.createRequest();\n+\t} catch (e) {}\n+\ttry {\n+\t\treturn new ActiveXObject(\"Msxml2.XMLHTTP\");\n+\t} catch (e) {}\n+\ttry {\n+\t\treturn new ActiveXObject(\"Microsoft.XMLHTTP\");\n+\t} catch (e) {}\n+\n+\treturn null;\n+}\n+\n+\n+/* ............................................................ */\n+/* time and data */\n+\n+/**\n+ * used to extract hours and minutes from timezone info, e.g '-0900'\n+ * @constant\n+ */\n+var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;\n+\n+/**\n+ * convert numeric timezone +/-ZZZZ to offset from UTC in seconds\n+ *\n+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n+ * @returns {Number} offset from UTC in seconds for timezone\n+ *\n+ * @globals tzRe\n+ */\n+function timezoneOffset(timezoneInfo) {\n+\tvar match = tzRe.exec(timezoneInfo);\n+\tvar tz_sign = (match[1] === '-' ? -1 : +1);\n+\tvar tz_hour = parseInt(match[2],10);\n+\tvar tz_min  = parseInt(match[3],10);\n+\n+\treturn tz_sign*(((tz_hour*60) + tz_min)*60);\n+}\n+\n+/**\n+ * return date in local time formatted in iso-8601 like format\n+ * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'\n+ *\n+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'\n+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n+ * @returns {String} date in local time in iso-8601 like format\n+ */\n+function formatDateISOLocal(epoch, timezoneInfo) {\n+\t// date corrected by timezone\n+\tvar localDate = new Date(1000 * (epoch +\n+\t\ttimezoneOffset(timezoneInfo)));\n+\tvar localDateStr = // e.g. '2005-08-07'\n+\t\tlocalDate.getUTCFullYear()                 + '-' +\n+\t\tpadLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +\n+\t\tpadLeft(localDate.getUTCDate(),    2, '0');\n+\tvar localTimeStr = // e.g. '21:49:46'\n+\t\tpadLeft(localDate.getUTCHours(),   2, '0') + ':' +\n+\t\tpadLeft(localDate.getUTCMinutes(), 2, '0') + ':' +\n+\t\tpadLeft(localDate.getUTCSeconds(), 2, '0');\n+\n+\treturn localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;\n+}\n+\n+\n+/* ............................................................ */\n+/* unquoting/unescaping filenames */\n+\n+/**#@+\n+ * @constant\n+ */\n+var escCodeRe = /\\\\([^0-7]|[0-7]{1,3})/g;\n+var octEscRe = /^[0-7]{1,3}$/;\n+var maybeQuotedRe = /^\\\"(.*)\\\"$/;\n+/**#@-*/\n+\n+/**\n+ * unquote maybe git-quoted filename\n+ * e.g. 'aa' -> 'aa', '\"a\\ta\"' -> 'a\ta'\n+ *\n+ * @param {String} str: git-quoted string\n+ * @returns {String} Unquoted and unescaped string\n+ *\n+ * @globals escCodeRe, octEscRe, maybeQuotedRe\n+ */\n+function unquote(str) {\n+\tfunction unq(seq) {\n+\t\tvar es = {\n+\t\t\t// character escape codes, aka escape sequences (from C)\n+\t\t\t// replacements are to some extent JavaScript specific\n+\t\t\tt: \"\\t\",   // tab            (HT, TAB)\n+\t\t\tn: \"\\n\",   // newline        (NL)\n+\t\t\tr: \"\\r\",   // return         (CR)\n+\t\t\tf: \"\\f\",   // form feed      (FF)\n+\t\t\tb: \"\\b\",   // backspace      (BS)\n+\t\t\ta: \"\\x07\", // alarm (bell)   (BEL)\n+\t\t\te: \"\\x1B\", // escape         (ESC)\n+\t\t\tv: \"\\v\"    // vertical tab   (VT)\n+\t\t};\n+\n+\t\tif (seq.search(octEscRe) !== -1) {\n+\t\t\t// octal char sequence\n+\t\t\treturn String.fromCharCode(parseInt(seq, 8));\n+\t\t} else if (seq in es) {\n+\t\t\t// C escape sequence, aka character escape code\n+\t\t\treturn es[seq];\n+\t\t}\n+\t\t// quoted ordinary character\n+\t\treturn seq;\n+\t}\n+\n+\tvar match = str.match(maybeQuotedRe);\n+\tif (match) {\n+\t\tstr = match[1];\n+\t\t// perhaps str = eval('\"'+str+'\"'); would be enough?\n+\t\tstr = str.replace(escCodeRe,\n+\t\t\tfunction (substr, p1, offset, s) { return unq(p1); });\n+\t}\n+\treturn str;\n+}\n+\n+/* end of common-lib.js */\n-- \n1.7.3\n"},{"id":"165899","messageId":"1302878645-458-3-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 02/11] gitweb.js: Update and improve comments in JavaScript files","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:43:56Z","receivedAt":"2011-04-15T14:43:56Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"This consists of adding a few extra explanation, fixing descriptions\nof functions to match names of parameters in code, adding a few\nseparators, and fixing spelling -- while at it spell 'neighbor' using\nAmerican spelling (and not as 'neighbour').\n\nThis is post-split cleanup.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nUnchanged from v1 version.\n\n gitweb/static/js/blame_incremental.js |   22 +++++++++++-----------\n gitweb/static/js/lib/common-lib.js    |   12 ++++++++----\n 2 files changed, 19 insertions(+), 15 deletions(-)\n\ndiff --git a/gitweb/static/js/blame_incremental.js b/gitweb/static/js/blame_incremental.js\nindex f63f78b..676da6b 100644\n--- a/gitweb/static/js/blame_incremental.js\n+++ b/gitweb/static/js/blame_incremental.js\n@@ -7,7 +7,7 @@\n  * @license GPLv2 or later\n  */\n \n-\n+/* ============================================================ */\n /*\n  * This code uses DOM methods instead of (nonstandard) innerHTML\n  * to modify page.\n@@ -26,7 +26,7 @@\n  */\n \n \n-/* ============================================================ */\n+/* ............................................................ */\n /* utility/helper functions (and variables) */\n \n var xhr;        // XMLHttpRequest object\n@@ -132,7 +132,7 @@ function writeTimeInterval() {\n }\n \n /**\n- * show an error message alert to user within page (in prohress info area)\n+ * show an error message alert to user within page (in progress info area)\n  * @param {String} str: plain text error message (no HTML)\n  *\n  * @globals div_progress_info\n@@ -182,7 +182,7 @@ function getColorNo(tr) {\n \n var colorsFreq = [0, 0, 0];\n /**\n- * return one of given possible colors (curently least used one)\n+ * return one of given possible colors (currently least used one)\n  * example: chooseColorNoFrom(2, 3) returns 2 or 3\n  *\n  * @param {Number[]} arguments: one or more numbers\n@@ -203,8 +203,8 @@ function chooseColorNoFrom() {\n }\n \n /**\n- * given two neigbour <tr> elements, find color which would be different\n- * from color of both of neighbours; used to 3-color blame table\n+ * given two neighbor <tr> elements, find color which would be different\n+ * from color of both of neighbors; used to 3-color blame table\n  *\n  * @param {HTMLElement} tr_prev\n  * @param {HTMLElement} tr_next\n@@ -216,14 +216,14 @@ function findColorNo(tr_prev, tr_next) {\n \tvar color_next = getColorNo(tr_next);\n \n \n-\t// neither of neighbours has color set\n+\t// neither of neighbors has color set\n \t// THEN we can use any of 3 possible colors\n \tif (!color_prev && !color_next) {\n \t\treturn chooseColorNoFrom(1,2,3);\n \t}\n \n-\t// either both neighbours have the same color,\n-\t// or only one of neighbours have color set\n+\t// either both neighbors have the same color,\n+\t// or only one of neighbors have color set\n \t// THEN we can use any color except given\n \tvar color;\n \tif (color_prev === color_next) {\n@@ -237,7 +237,7 @@ function findColorNo(tr_prev, tr_next) {\n \t\treturn chooseColorNoFrom((color % 3) + 1, ((color+1) % 3) + 1);\n \t}\n \n-\t// neighbours have different colors\n+\t// neighbors have different colors\n \t// THEN there is only one color left\n \treturn (3 - ((color_prev + color_next) % 3));\n }\n@@ -258,7 +258,7 @@ function isStartOfGroup(tr) {\n \n /**\n  * change colors to use zebra coloring (2 colors) instead of 3 colors\n- * concatenate neighbour commit groups belonging to the same commit\n+ * concatenate neighbor commit groups belonging to the same commit\n  *\n  * @globals colorRe\n  */\ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nindex 38f3b9e..6a6d200 100644\n--- a/gitweb/static/js/lib/common-lib.js\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -13,14 +13,17 @@\n /* Padding */\n \n /**\n- * pad number N with nonbreakable spaces on the left, to WIDTH characters\n+ * pad INPUT on the left with STR that is assumed to have visible\n+ * width of single character (for example nonbreakable spaces),\n+ * to WIDTH characters\n+ *\n  * example: padLeftStr(12, 3, '\\u00A0') == '\\u00A012'\n  *          ('\\u00A0' is nonbreakable space)\n  *\n  * @param {Number|String} input: number to pad\n  * @param {Number} width: visible width of output\n  * @param {String} str: string to prefix to string, e.g. '\\u00A0'\n- * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR\n+ * @returns {String} INPUT prefixed with STR x (WIDTH - INPUT.length)\n  */\n function padLeftStr(input, width, str) {\n \tvar prefix = '';\n@@ -34,7 +37,7 @@ function padLeftStr(input, width, str) {\n }\n \n /**\n- * Pad INPUT on the left to SIZE width, using given padding character CH,\n+ * Pad INPUT on the left to WIDTH, using given padding character CH,\n  * for example padLeft('a', 3, '_') is '__a'.\n  *\n  * @param {String} input: input value converted to string.\n@@ -140,7 +143,8 @@ var maybeQuotedRe = /^\\\"(.*)\\\"$/;\n /**#@-*/\n \n /**\n- * unquote maybe git-quoted filename\n+ * unquote maybe C-quoted filename (as used by git, i.e. it is\n+ * in double quotes '\"' if there is any escape character used)\n  * e.g. 'aa' -> 'aa', '\"a\\ta\"' -> 'a\ta'\n  *\n  * @param {String} str: git-quoted string\n-- \n1.7.3\n"},{"id":"165897","messageId":"1302878645-458-4-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 03/11] gitweb.js: Provide default values for padding in padLeftStr and padLeft","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:43:57Z","receivedAt":"2011-04-15T14:43:57Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"This means that one can use padLeft(4, 2) and it would be equivalent\nto runing padLeft(4, 2, '0'), and it would return '04' i.e. '4' padded\nwith '0' to width 2, to be used e.g. in formatting date and time.\n\nThis should make those functions easier to use.  Current code doesn't\nyet make use of this feature.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is unchanged from v1 version.\n\nThis patch is not strictly necessary, but it makes those functions\neasier to use, I guess.  Removing this patch would require though some\ntrivially resolvable changes to subsequent commits in this series.\n\n gitweb/static/js/lib/common-lib.js |   14 +++++++++++---\n 1 files changed, 11 insertions(+), 3 deletions(-)\n\ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nindex 6a6d200..c45454e 100644\n--- a/gitweb/static/js/lib/common-lib.js\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -22,11 +22,14 @@\n  *\n  * @param {Number|String} input: number to pad\n  * @param {Number} width: visible width of output\n- * @param {String} str: string to prefix to string, e.g. '\\u00A0'\n+ * @param {String} str: string to prefix to string, defaults to '\\u00A0'\n  * @returns {String} INPUT prefixed with STR x (WIDTH - INPUT.length)\n  */\n function padLeftStr(input, width, str) {\n \tvar prefix = '';\n+\tif (typeof str === 'undefined') {\n+\t\tch = '\\u00A0'; // using '&nbsp;' doesn't work in all browsers\n+\t}\n \n \twidth -= input.toString().length;\n \twhile (width > 0) {\n@@ -38,16 +41,21 @@ function padLeftStr(input, width, str) {\n \n /**\n  * Pad INPUT on the left to WIDTH, using given padding character CH,\n- * for example padLeft('a', 3, '_') is '__a'.\n+ * for example padLeft('a', 3, '_') is '__a'\n+ *             padLeft(4, 2) is '04' (same as padLeft(4, 2, '0'))\n  *\n  * @param {String} input: input value converted to string.\n  * @param {Number} width: desired length of output.\n- * @param {String} ch: single character to prefix to string.\n+ * @param {String} ch: single character to prefix to string, defaults to '0'.\n  *\n  * @returns {String} Modified string, at least SIZE length.\n  */\n function padLeft(input, width, ch) {\n \tvar s = input + \"\";\n+\tif (typeof ch === 'undefined') {\n+\t\tch = '0';\n+\t}\n+\n \twhile (s.length < width) {\n \t\ts = ch + s;\n \t}\n-- \n1.7.3\n"},{"id":"165893","messageId":"1302878645-458-5-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 04/11] gitweb.js: Extract and improve datetime handling","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:43:58Z","receivedAt":"2011-04-15T14:43:58Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Move formatDateISOLocal(epoch, timezone) function (and also helper\ntimezoneOffset(timezoneInfo) function it requires) from common-lib.js to\ndatetime.js\n\nAdd new functions:\n* localTimezoneOffset - to get browser timezone offset in seconds\n* localTimezoneInfo   - to get browser timezone in '(+|-)HHMM' format\n* formatTimezoneInfo - turn offset in hours and minutes into '(+|-)HHMM'\n* parseRFC2822Date - to parse RFC-2822 dates that gitweb uses into epoch\n* formatDateRFC2882 - like formatDateISOLocal, only RFC-2822 format\n\nAll those functions are meant to be used in future commit\n'gitweb: javascript ability to adjust time based on timezone'\n\nAn alternative would be to use e.g. Datejs (http://www.datejs.com)\nlibrary.\n\n\nWhile at it escape '-' in character class inside tzRe regexp, as\nrecommended by JSLint (http://www.jslint.com).\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nUnchanged from v1 version.\n\n gitweb/Makefile                    |    1 +\n gitweb/static/js/lib/common-lib.js |   51 -----------\n gitweb/static/js/lib/datetime.js   |  161 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 162 insertions(+), 51 deletions(-)\n create mode 100644 gitweb/static/js/lib/datetime.js\n\ndiff --git a/gitweb/Makefile b/gitweb/Makefile\nindex 0baa9df..403265a 100644\n--- a/gitweb/Makefile\n+++ b/gitweb/Makefile\n@@ -117,6 +117,7 @@ GITWEB_FILES += static/git-logo.png static/git-favicon.png\n # js/lib/common-lib.js should be always first, then js/lib/*.js,\n # then the rest of files; js/gitweb.js should be last (if it exists)\n GITWEB_JSLIB_FILES += static/js/lib/common-lib.js\n+GITWEB_JSLIB_FILES += static/js/lib/datetime.js\n GITWEB_JSLIB_FILES += static/js/javascript-detection.js\n GITWEB_JSLIB_FILES += static/js/blame_incremental.js\n \ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nindex c45454e..d6b0c0d 100644\n--- a/gitweb/static/js/lib/common-lib.js\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -89,57 +89,6 @@ function createRequestObject() {\n \n \n /* ............................................................ */\n-/* time and data */\n-\n-/**\n- * used to extract hours and minutes from timezone info, e.g '-0900'\n- * @constant\n- */\n-var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/;\n-\n-/**\n- * convert numeric timezone +/-ZZZZ to offset from UTC in seconds\n- *\n- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n- * @returns {Number} offset from UTC in seconds for timezone\n- *\n- * @globals tzRe\n- */\n-function timezoneOffset(timezoneInfo) {\n-\tvar match = tzRe.exec(timezoneInfo);\n-\tvar tz_sign = (match[1] === '-' ? -1 : +1);\n-\tvar tz_hour = parseInt(match[2],10);\n-\tvar tz_min  = parseInt(match[3],10);\n-\n-\treturn tz_sign*(((tz_hour*60) + tz_min)*60);\n-}\n-\n-/**\n- * return date in local time formatted in iso-8601 like format\n- * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'\n- *\n- * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'\n- * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n- * @returns {String} date in local time in iso-8601 like format\n- */\n-function formatDateISOLocal(epoch, timezoneInfo) {\n-\t// date corrected by timezone\n-\tvar localDate = new Date(1000 * (epoch +\n-\t\ttimezoneOffset(timezoneInfo)));\n-\tvar localDateStr = // e.g. '2005-08-07'\n-\t\tlocalDate.getUTCFullYear()                 + '-' +\n-\t\tpadLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +\n-\t\tpadLeft(localDate.getUTCDate(),    2, '0');\n-\tvar localTimeStr = // e.g. '21:49:46'\n-\t\tpadLeft(localDate.getUTCHours(),   2, '0') + ':' +\n-\t\tpadLeft(localDate.getUTCMinutes(), 2, '0') + ':' +\n-\t\tpadLeft(localDate.getUTCSeconds(), 2, '0');\n-\n-\treturn localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;\n-}\n-\n-\n-/* ............................................................ */\n /* unquoting/unescaping filenames */\n \n /**#@+\ndiff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js\nnew file mode 100644\nindex 0000000..b3dcedb\n--- /dev/null\n+++ b/gitweb/static/js/lib/datetime.js\n@@ -0,0 +1,161 @@\n+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>\n+//               2007, Petr Baudis <pasky@suse.cz>\n+//          2008-2011, Jakub Narebski <jnareb@gmail.com>\n+\n+/**\n+ * @fileOverview Datetime manipulation: parsing and formatting\n+ * @license GPLv2 or later\n+ */\n+\n+\n+/* ............................................................ */\n+/* parsing and retrieving datetime related information */\n+\n+/**\n+ * used to extract hours and minutes from timezone info, e.g '-0900'\n+ * @constant\n+ */\n+var tzRe = /^([+\\-])([0-9][0-9])([0-9][0-9])$/;\n+\n+/**\n+ * convert numeric timezone +/-ZZZZ to offset from UTC in seconds\n+ *\n+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n+ * @returns {Number} offset from UTC in seconds for timezone\n+ *\n+ * @globals tzRe\n+ */\n+function timezoneOffset(timezoneInfo) {\n+\tvar match = tzRe.exec(timezoneInfo);\n+\tvar tz_sign = (match[1] === '-' ? -1 : +1);\n+\tvar tz_hour = parseInt(match[2],10);\n+\tvar tz_min  = parseInt(match[3],10);\n+\n+\treturn tz_sign*(((tz_hour*60) + tz_min)*60);\n+}\n+\n+/**\n+ * return local (browser) timezone as offset from UTC in seconds\n+ *\n+ * @returns {Number} offset from UTC in seconds for local timezone\n+ */\n+function localTimezoneOffset() {\n+\t// getTimezoneOffset returns the time-zone offset from UTC,\n+\t// in _minutes_, for the current locale\n+\treturn ((new Date()).getTimezoneOffset() * -60);\n+}\n+\n+/**\n+ * return local (browser) timezone as numeric timezone '(+|-)HHMM'\n+ *\n+ * @returns {String} locat timezone as -/+ZZZZ\n+ */\n+function localTimezoneInfo() {\n+\tvar tzOffsetMinutes = (new Date()).getTimezoneOffset() * -1;\n+\n+\treturn formatTimezoneInfo(0, tzOffsetMinutes);\n+}\n+\n+\n+/**\n+ * Parse RFC-2822 date into a Unix timestamp (into epoch)\n+ *\n+ * @param {String} date: date in RFC-2822 format, e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'\n+ * @returns {Number} epoch i.e. seconds since '00:00:00 1970-01-01 UTC'\n+ */\n+function parseRFC2822Date(date) {\n+\t// Date.parse accepts the IETF standard (RFC 1123 Section 5.2.14 and elsewhere)\n+\t// date syntax, which is defined in RFC 2822 (obsoletes RFC 822)\n+\t// and returns number of _milli_seconds since January 1, 1970, 00:00:00 UTC\n+\treturn Date.parse(date) / 1000;\n+}\n+\n+\n+/* ............................................................ */\n+/* formatting date */\n+\n+/**\n+ * format timezone offset as numerical timezone '(+|-)HHMM' or '(+|-)HH:MM'\n+ *\n+ * @param {Number} hours:    offset in hours, e.g. 2 for '+0200'\n+ * @param {Number} [minutes] offset in minutes, e.g. 30 for '-4030';\n+ *                           it is split into hours if not 0 <= minutes < 60,\n+ *                           for example 1200 would give '+0100';\n+ *                           defaults to 0\n+ * @param {String} [sep] separator between hours and minutes part,\n+ *                       default is '', might be ':' for W3CDTF (rfc-3339)\n+ * @returns {String} timezone in '(+|-)HHMM' or '(+|-)HH:MM' format\n+ */\n+function formatTimezoneInfo(hours, minutes, sep) {\n+\tminutes = minutes || 0; // to be able to use formatTimezoneInfo(hh)\n+\tsep = sep || ''; // default format is +/-ZZZZ\n+\n+\tif (minutes < 0 || minutes > 59) {\n+\t\thours = minutes > 0 ? Math.floor(minutes / 60) : Math.ceil(minutes / 60);\n+\t\tminutes = Math.abs(minutes - 60*hours); // sign of minutes is sign of hours\n+\t\t// NOTE: this works correctly because there is no UTC-00:30 timezone\n+\t}\n+\n+\tvar tzSign = hours >= 0 ? '+' : '-';\n+\tif (hours < 0) {\n+\t\thours = -hours; // sign is stored in tzSign\n+\t}\n+\n+\treturn tzSign + padLeft(hours, 2, '0') + sep + padLeft(minutes, 2, '0');\n+}\n+\n+/**\n+ * return date in local time formatted in iso-8601 like format\n+ * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'\n+ *\n+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'\n+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n+ * @returns {String} date in local time in iso-8601 like format\n+ */\n+function formatDateISOLocal(epoch, timezoneInfo) {\n+\t// date corrected by timezone\n+\tvar localDate = new Date(1000 * (epoch +\n+\t\ttimezoneOffset(timezoneInfo)));\n+\tvar localDateStr = // e.g. '2005-08-07'\n+\t\tlocalDate.getUTCFullYear()                 + '-' +\n+\t\tpadLeft(localDate.getUTCMonth()+1, 2, '0') + '-' +\n+\t\tpadLeft(localDate.getUTCDate(),    2, '0');\n+\tvar localTimeStr = // e.g. '21:49:46'\n+\t\tpadLeft(localDate.getUTCHours(),   2, '0') + ':' +\n+\t\tpadLeft(localDate.getUTCMinutes(), 2, '0') + ':' +\n+\t\tpadLeft(localDate.getUTCSeconds(), 2, '0');\n+\n+\treturn localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;\n+}\n+\n+/**\n+ * return date in local time formatted in rfc-2822 format\n+ * e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'\n+ *\n+ * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC'\n+ * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM'\n+ * @param {Boolean} [padDay] e.g. 'Sun, 07 Aug' if true, 'Sun, 7 Aug' otherwise\n+ * @returns {String} date in local time in rfc-2822 format\n+ */\n+function formatDateRFC2882(epoch, timezoneInfo, padDay) {\n+\t// A short textual representation of a month, three letters\n+\tvar months = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n+\t// A textual representation of a day, three letters\n+\tvar days = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n+\t// date corrected by timezone\n+\tvar localDate = new Date(1000 * (epoch +\n+\t\ttimezoneOffset(timezoneInfo)));\n+\tvar localDateStr = // e.g. 'Sun, 7 Aug 2005' or 'Sun, 07 Aug 2005'\n+\t\tdays[localDate.getUTCDay()] + ', ' +\n+\t\t(padDay ? padLeft(localDate.getUTCDate(),2,'0') : localDate.getUTCDate()) + ' ' +\n+\t\tmonths[localDate.getUTCMonth()] + ' ' +\n+\t\tlocalDate.getUTCFullYear();\n+\tvar localTimeStr = // e.g. '21:49:46'\n+\t\tpadLeft(localDate.getUTCHours(),   2, '0') + ':' +\n+\t\tpadLeft(localDate.getUTCMinutes(), 2, '0') + ':' +\n+\t\tpadLeft(localDate.getUTCSeconds(), 2, '0');\n+\n+\treturn localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo;\n+}\n+\n+/* end of datetime.js */\n-- \n1.7.3\n"},{"id":"165894","messageId":"1302878645-458-6-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 05/11] gitweb.js: Introduce gitweb/static/js/lib/cookies.js","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:43:59Z","receivedAt":"2011-04-15T14:43:59Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"This file provides functions for setting, getting and deleting\ncookies.\n\nCode taken from subsection \"Cookies in JavaScript\" of \"Professional\nJavaScript for Web Developers\" by Nicholas C. Zakas and from cookie\nplugin for jQuery (dual licensed under the MIT and GPL licenses).\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nUnchanged from v1 version... \n\nAgain, the summary line (subject) for this commit could be better.\nFor example:\n\n  gitweb.js: Library for handling cookies\n\n gitweb/Makefile                 |    1 +\n gitweb/static/js/lib/cookies.js |  114 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 115 insertions(+), 0 deletions(-)\n create mode 100644 gitweb/static/js/lib/cookies.js\n\ndiff --git a/gitweb/Makefile b/gitweb/Makefile\nindex 403265a..7dd1dee 100644\n--- a/gitweb/Makefile\n+++ b/gitweb/Makefile\n@@ -118,6 +118,7 @@ GITWEB_FILES += static/git-logo.png static/git-favicon.png\n # then the rest of files; js/gitweb.js should be last (if it exists)\n GITWEB_JSLIB_FILES += static/js/lib/common-lib.js\n GITWEB_JSLIB_FILES += static/js/lib/datetime.js\n+GITWEB_JSLIB_FILES += static/js/lib/cookies.js\n GITWEB_JSLIB_FILES += static/js/javascript-detection.js\n GITWEB_JSLIB_FILES += static/js/blame_incremental.js\n \ndiff --git a/gitweb/static/js/lib/cookies.js b/gitweb/static/js/lib/cookies.js\nnew file mode 100644\nindex 0000000..72b51cd\n--- /dev/null\n+++ b/gitweb/static/js/lib/cookies.js\n@@ -0,0 +1,114 @@\n+/**\n+ * @fileOverview Accessing cookies from JavaScript\n+ * @license GPLv2 or later\n+ */\n+\n+/*\n+ * Based on subsection \"Cookies in JavaScript\" of \"Professional\n+ * JavaScript for Web Developers\" by Nicholas C. Zakas and cookie\n+ * plugin from jQuery (dual licensed under the MIT and GPL licenses)\n+ */\n+\n+\n+/**\n+ * Create a cookie with the given name and value,\n+ * and other optional parameters.\n+ *\n+ * @example\n+ *   setCookie('foo', 'bar'); // will be deleted when browser exits\n+ *   setCookie('foo', 'bar', { expires: new Date(Date.parse('Jan 1, 2012')) });\n+ *   setCookie('foo', 'bar', { expires: 7 }); // 7 days = 1 week\n+ *   setCookie('foo', 'bar', { expires: 14, path: '/' });\n+ *\n+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores).\n+ * @param {String} sValue:   The string value stored in a cookie.\n+ * @param {Object} [options] An object literal containing key/value pairs\n+ *                           to provide optional cookie attributes.\n+ * @param {String|Number|Date} [options.expires] Either literal string to be used as cookie expires,\n+ *                            or an integer specifying the expiration date from now on in days,\n+ *                            or a Date object to be used as cookie expiration date.\n+ *                            If a negative value is specified or a date in the past),\n+ *                            the cookie will be deleted.\n+ *                            If set to null or omitted, the cookie will be a session cookie\n+ *                            and will not be retained when the the browser exits.\n+ * @param {String} [options.path] Restrict access of a cookie to particular directory\n+ *                               (default: path of page that created the cookie).\n+ * @param {String} [options.domain] Override what web sites are allowed to access cookie\n+ *                                  (default: domain of page that created the cookie).\n+ * @param {Boolean} [options.secure] If true, the secure attribute of the cookie will be set\n+ *                                   and the cookie would be accessible only from secure sites\n+ *                                   (cookie transmission will require secure protocol like HTTPS).\n+ */\n+function setCookie(sName, sValue, options) {\n+\toptions = options || {};\n+\tif (sValue === null) {\n+\t\tsValue = '';\n+\t\toption.expires = 'delete';\n+\t}\n+\n+\tvar sCookie = sName + '=' + encodeURIComponent(sValue);\n+\n+\tif (options.expires) {\n+\t\tvar oExpires = options.expires, sDate;\n+\t\tif (oExpires === 'delete') {\n+\t\t\tsDate = 'Thu, 01 Jan 1970 00:00:00 GMT';\n+\t\t} else if (typeof oExpires === 'string') {\n+\t\t\tsDate = oExpires;\n+\t\t} else {\n+\t\t\tvar oDate;\n+\t\t\tif (typeof oExpires === 'number') {\n+\t\t\t\toDate = new Date();\n+\t\t\t\toDate.setTime(oDate.getTime() + (oExpires * 24 * 60 * 60 * 1000)); // days to ms\n+\t\t\t} else {\n+\t\t\t\toDate = oExpires;\n+\t\t\t}\n+\t\t\tsDate = oDate.toGMTString();\n+\t\t}\n+\t\tsCookie += '; expires=' + sDate;\n+\t}\n+\n+\tif (options.path) {\n+\t\tsCookie += '; path=' + (options.path);\n+\t}\n+\tif (options.domain) {\n+\t\tsCookie += '; domain=' + (options.domain);\n+\t}\n+\tif (options.secure) {\n+\t\tsCookie += '; secure';\n+\t}\n+\tdocument.cookie = sCookie;\n+}\n+\n+/**\n+ * Get the value of a cookie with the given name.\n+ *\n+ * @param {String} sName: Unique name of a cookie (letters, numbers, underscores)\n+ * @returns {String|null} The string value stored in a cookie\n+ */\n+function getCookie(sName) {\n+\tvar sRE = '(?:; )?' + sName + '=([^;]*);?';\n+\tvar oRE = new RegExp(sRE);\n+\tif (oRE.test(document.cookie)) {\n+\t\treturn decodeURIComponent(RegExp['$1']);\n+\t} else {\n+\t\treturn null;\n+\t}\n+}\n+\n+/**\n+ * Delete cookie with given name\n+ *\n+ * @param {String} sName:    Unique name of a cookie (letters, numbers, underscores)\n+ * @param {Object} [options] An object literal containing key/value pairs\n+ *                           to provide optional cookie attributes.\n+ * @param {String} [options.path]   Must be the same as when setting a cookie\n+ * @param {String} [options.domain] Must be the same as when setting a cookie\n+ */\n+function deleteCookie(sName, options) {\n+\toptions = options || {};\n+\toptions.expires = 'delete';\n+\n+\tsetCookie(sName, '', options);\n+}\n+\n+/* end of cookies.js */\n-- \n1.7.3\n"},{"id":"165901","messageId":"1302878645-458-7-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 06/11] gitweb.js: Provide getElementsByClassName method (if it not exists)","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:44:00Z","receivedAt":"2011-04-15T14:44:00Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"The code is simplified and does not support full specification of\nnative getElementsByClassName method, but implements just subset that\nwould be enough for gitweb, supporting only single class name.\n\nThis patch makes subsequent timezone manipulation work on Mozilla\n1.17.2 which doesn't have document.getElementsByClassName (while\nnative implementation is used e.g. for Firefox 4.0).\n\nSigned-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is unchanged from v1 version, except added commentary in\nthe commit message.\n\n gitweb/static/js/lib/common-lib.js |   51 ++++++++++++++++++++++++++++++++++++\n 1 files changed, 51 insertions(+), 0 deletions(-)\n\ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nindex d6b0c0d..b371391 100644\n--- a/gitweb/static/js/lib/common-lib.js\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -89,6 +89,57 @@ function createRequestObject() {\n \n \n /* ............................................................ */\n+/* Support for legacy browsers */\n+\n+/**\n+ * Provides getElementsByClassName method, if there is no native\n+ * implementation of this method.\n+ *\n+ * NOTE that there are limits and differences compared to native\n+ * getElementsByClassName as defined by e.g.:\n+ *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName\n+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname\n+ *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname\n+ *\n+ * Namely, this implementation supports only single class name as\n+ * argument and not set of space-separated tokens representing classes,\n+ * it returns Array of nodes rather than live NodeList, and has\n+ * additional optional argument where you can limit search to given tags\n+ * (via getElementsByTagName).\n+ *\n+ * Based on\n+ *   http://code.google.com/p/getelementsbyclassname/\n+ *   http://www.dustindiaz.com/getelementsbyclass/\n+ *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript\n+ *\n+ * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/\n+ *\n+ * @param {String} class: name of _single_ class to find\n+ * @param {String} [taghint] limit search to given tags\n+ * @returns {Node[]} array of matching elements\n+ */\n+if (!('getElementsByClassName' in document)) {\n+\tdocument.getElementsByClassName = function (classname, taghint) {\n+\t\ttaghint = taghint || \"*\";\n+\t\tvar elements = (taghint === \"*\" && document.all) ?\n+\t\t               document.all :\n+\t\t               document.getElementsByTagName(taghint);\n+\t\tvar pattern = new RegExp(\"(^|\\\\s)\" + classname + \"(\\\\s|$)\");\n+\t\tvar matches= [];\n+\t\tfor (var i = 0, j = 0, n = elements.length; i < n; i++) {\n+\t\t\tvar el= elements[i];\n+\t\t\tif (el.className && pattern.test(el.className)) {\n+\t\t\t\t// matches.push(el);\n+\t\t\t\tmatches[j] = el;\n+\t\t\t\tj++;\n+\t\t\t}\n+\t\t}\n+\t\treturn matches;\n+\t};\n+} // end if\n+\n+\n+/* ............................................................ */\n /* unquoting/unescaping filenames */\n \n /**#@+\n-- \n1.7.3\n"},{"id":"165892","messageId":"1302878645-458-8-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 07/11] gitweb: Refactor generating of long dates into format_timestamp_html","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:44:01Z","receivedAt":"2011-04-15T14:44:01Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"It is pure refactoring and doesn't change gitweb output, though this\ncould potentially affect 'summary', 'log', and 'commit'-like views\n('commit', 'commitdiff', 'tag').\n\nRemove print_local_time and format_local_time, as their use is now\nreplaced (indirectly) by using format_timestamp_html.\n\n\nWhile at it improve whitespace formatting.\n\nInspired-by-code-by: Kevin Cernekee <cernekee@gmail.com>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is unchanged from previous v1 version.\n\nNote that this patch could be squashed with the next patch in series,\nbut I think that split version (pure refactoring + change in behavior)\nis easier to review.\n\n gitweb/gitweb.perl |   45 ++++++++++++++++++++++-----------------------\n 1 files changed, 22 insertions(+), 23 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex ee69ea6..7329db2 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3938,22 +3938,21 @@ sub git_print_section {\n \tprint $cgi->end_div;\n }\n \n-sub print_local_time {\n-\tprint format_local_time(@_);\n-}\n+sub format_timestamp_html {\n+\tmy ($date, %opts) = @_;\n+\tmy $strtime = $date->{'rfc2822'};\n \n-sub format_local_time {\n-\tmy $localtime = '';\n-\tmy %date = @_;\n-\tif ($date{'hour_local'} < 6) {\n-\t\t$localtime .= sprintf(\" (<span class=\\\"atnight\\\">%02d:%02d</span> %s)\",\n-\t\t\t$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});\n-\t} else {\n-\t\t$localtime .= sprintf(\" (%02d:%02d %s)\",\n-\t\t\t$date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});\n+\treturn $strtime unless $opts{'-localtime'};\n+\n+\tmy $localtime_format = '(%02d:%02d %s)';\n+\tif ($date->{'hour_local'} < 6) {\n+\t\t$localtime_format = '(<span class=\"atnight\">%02d:%02d</span> %s)';\n \t}\n+\t$strtime .= ' ' .\n+\t            sprintf($localtime_format,\n+\t                    $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'});\n \n-\treturn $localtime;\n+\treturn $strtime;\n }\n \n # Outputs the author name and date in long form\n@@ -3966,10 +3965,9 @@ sub git_print_authorship {\n \tmy %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});\n \tprint \"<$tag class=\\\"author_date\\\">\" .\n \t      format_search_author($author, \"author\", esc_html($author)) .\n-\t      \" [$ad{'rfc2822'}\";\n-\tprint_local_time(%ad) if ($opts{-localtime});\n-\tprint \"]\" . git_get_avatar($co->{'author_email'}, -pad_before => 1)\n-\t\t  . \"</$tag>\\n\";\n+\t      \" [\".format_timestamp_html(\\%ad, %opts).\"]\".\n+\t      git_get_avatar($co->{'author_email'}, -pad_before => 1) .\n+\t      \"</$tag>\\n\";\n }\n \n # Outputs table rows containing the full author or committer information,\n@@ -3986,16 +3984,16 @@ sub git_print_authorship_rows {\n \t\tmy %wd = parse_date($co->{\"${who}_epoch\"}, $co->{\"${who}_tz\"});\n \t\tprint \"<tr><td>$who</td><td>\" .\n \t\t      format_search_author($co->{\"${who}_name\"}, $who,\n-\t\t\t       esc_html($co->{\"${who}_name\"})) . \" \" .\n+\t\t                           esc_html($co->{\"${who}_name\"})) . \" \" .\n \t\t      format_search_author($co->{\"${who}_email\"}, $who,\n-\t\t\t       esc_html(\"<\" . $co->{\"${who}_email\"} . \">\")) .\n+\t\t                           esc_html(\"<\" . $co->{\"${who}_email\"} . \">\")) .\n \t\t      \"</td><td rowspan=\\\"2\\\">\" .\n \t\t      git_get_avatar($co->{\"${who}_email\"}, -size => 'double') .\n \t\t      \"</td></tr>\\n\" .\n \t\t      \"<tr>\" .\n-\t\t      \"<td></td><td> $wd{'rfc2822'}\";\n-\t\tprint_local_time(%wd);\n-\t\tprint \"</td>\" .\n+\t\t      \"<td></td><td>\" .\n+\t\t      format_timestamp_html(\\%wd, -localtime=>1) .\n+\t\t      \"</td>\" .\n \t\t      \"</tr>\\n\";\n \t}\n }\n@@ -5410,7 +5408,8 @@ sub git_summary {\n \t      \"<tr id=\\\"metadata_desc\\\"><td>description</td><td>\" . esc_html($descr) . \"</td></tr>\\n\" .\n \t      \"<tr id=\\\"metadata_owner\\\"><td>owner</td><td>\" . esc_html($owner) . \"</td></tr>\\n\";\n \tif (defined $cd{'rfc2822'}) {\n-\t\tprint \"<tr id=\\\"metadata_lchange\\\"><td>last change</td><td>$cd{'rfc2822'}</td></tr>\\n\";\n+\t\tprint \"<tr id=\\\"metadata_lchange\\\"><td>last change</td>\" .\n+\t\t      \"<td>\".format_timestamp_html(\\%cd).\"</td></tr>\\n\";\n \t}\n \n \t# use per project git URL list in $projectroot/$project/cloneurl\n-- \n1.7.3\n"},{"id":"165895","messageId":"1302878645-458-9-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 08/11] gitweb: Unify the way long timestamp is displayed","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:44:02Z","receivedAt":"2011-04-15T14:44:02Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"format_timestamp_html loses its \"-localtime => 1\" option, and now\nalways print the local time (in author/comitter/tagger local\ntimezone), with \"atnight\" warning if needed.\n\nThis means that both 'summary' and 'log' views now display localtime.\nIn the case of 'log' view this can be thought as an improvement, as\nnow one can easily see which commits in a series are made \"atnight\"\nand should be examined closer.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis patch is unchanged from v1 version.\n\nIt was marked in previous version of this series as an RFC, because I\nwasn't sure about the look of gitweb after this patch... but it grew\nup on me (well, I have a few doubts about new look of 'summary' page).\n\n gitweb/gitweb.perl |    8 +++-----\n 1 files changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 7329db2..67bcfe8 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3939,11 +3939,9 @@ sub git_print_section {\n }\n \n sub format_timestamp_html {\n-\tmy ($date, %opts) = @_;\n+\tmy $date = shift;\n \tmy $strtime = $date->{'rfc2822'};\n \n-\treturn $strtime unless $opts{'-localtime'};\n-\n \tmy $localtime_format = '(%02d:%02d %s)';\n \tif ($date->{'hour_local'} < 6) {\n \t\t$localtime_format = '(<span class=\"atnight\">%02d:%02d</span> %s)';\n@@ -3965,7 +3963,7 @@ sub git_print_authorship {\n \tmy %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});\n \tprint \"<$tag class=\\\"author_date\\\">\" .\n \t      format_search_author($author, \"author\", esc_html($author)) .\n-\t      \" [\".format_timestamp_html(\\%ad, %opts).\"]\".\n+\t      \" [\".format_timestamp_html(\\%ad).\"]\".\n \t      git_get_avatar($co->{'author_email'}, -pad_before => 1) .\n \t      \"</$tag>\\n\";\n }\n@@ -3992,7 +3990,7 @@ sub git_print_authorship_rows {\n \t\t      \"</td></tr>\\n\" .\n \t\t      \"<tr>\" .\n \t\t      \"<td></td><td>\" .\n-\t\t      format_timestamp_html(\\%wd, -localtime=>1) .\n+\t\t      format_timestamp_html(\\%wd) .\n \t\t      \"</td>\" .\n \t\t      \"</tr>\\n\";\n \t}\n-- \n1.7.3\n"},{"id":"165896","messageId":"1302878645-458-10-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH 09/11] gitweb: JavaScript ability to adjust time based on timezone","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:44:03Z","receivedAt":"2011-04-15T14:44:03Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\n\nThis patch is based on Kevin Cernekee's <cernekee@gmail.com>\npatch series entitled \"gitweb: introduce localtime feature\".  While\nKevin's patch changed the server side output so that the timezone\nwas output from gitweb itself, this has a number of drawbacks, in\nparticular with respect to gitweb-caching.\n\nThis patch takes the same basic goal, display the appropriate times in\na given common timezone, and implements it in JavaScript.  This\nrequires adding / using a new class, \"datetime\", to be able to find\nelements to be adjusted from JavaScript.  Appropriate dates are\nwrapped in a span with this class.\n\nTimezone to be used can be retrieved from \"gitweb_tz\" cookie, though\ncurrently there is no way to set / manipulate this cookie from gitweb;\nthis is left for later commit.\n\nValid timezones, currently, are: \"utc\", \"local\" (which means that\ntimezone is taken from browser), and \"+/-ZZZZ\" numeric timezone as in\nRFC-2822.  Default timezone is \"local\" (currently not configurable,\nleft for later commit).\n\nFallback (should JavaScript not be enabled) is to treat dates as they\nhave been and display them, only, in UTC.\n\nPages affected:\n* 'summary' view, \"last change\" field (commit time from latest change)\n* 'log' view, author time\n* 'commit' and 'commitdiff' views, author/committer time\n* 'tag' view, tagger time\n\nBased-on-code-from: Kevin Cernekee <cernekee@gmail.com>\nSigned-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThe only change from v1 is fixing typo 'utf' -> 'utc' timezone.\n\n gitweb/Makefile                     |    1 +\n gitweb/gitweb.perl                  |   11 +++++--\n gitweb/static/js/adjust-timezone.js |   59 +++++++++++++++++++++++++++++++++++\n gitweb/static/js/lib/datetime.js    |   15 +++++++++\n 4 files changed, 83 insertions(+), 3 deletions(-)\n create mode 100644 gitweb/static/js/adjust-timezone.js\n\ndiff --git a/gitweb/Makefile b/gitweb/Makefile\nindex 7dd1dee..5d20515 100644\n--- a/gitweb/Makefile\n+++ b/gitweb/Makefile\n@@ -120,6 +120,7 @@ GITWEB_JSLIB_FILES += static/js/lib/common-lib.js\n GITWEB_JSLIB_FILES += static/js/lib/datetime.js\n GITWEB_JSLIB_FILES += static/js/lib/cookies.js\n GITWEB_JSLIB_FILES += static/js/javascript-detection.js\n+GITWEB_JSLIB_FILES += static/js/adjust-timezone.js\n GITWEB_JSLIB_FILES += static/js/blame_incremental.js\n \n \ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 67bcfe8..6651946 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3732,9 +3732,14 @@ sub git_footer_html {\n \t\t      qq!startBlame(\"!. href(action=>\"blame_data\", -replay=>1) .qq!\",\\n!.\n \t\t      qq!           \"!. href() .qq!\");\\n!.\n \t\t      qq!</script>\\n!;\n-\t} elsif (gitweb_check_feature('javascript-actions')) {\n+\t} else {\n \t\tprint qq!<script type=\"text/javascript\">\\n!.\n-\t\t      qq!window.onload = fixLinks;\\n!.\n+\t\t      qq!window.onload = function () {\\n!.\n+\t\t      (gitweb_check_feature('javascript-actions') ?\n+\t\t      qq!\tfixLinks();\\n! : '').\n+\t\t      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html\n+\t\t      qq!\tonloadTZSetup('local', 'gitweb_tz', 'datetime');\\n!.\n+\t\t      qq!};\\n!.\n \t\t      qq!</script>\\n!;\n \t}\n \n@@ -3940,7 +3945,7 @@ sub git_print_section {\n \n sub format_timestamp_html {\n \tmy $date = shift;\n-\tmy $strtime = $date->{'rfc2822'};\n+\tmy $strtime = '<span class=\"datetime\">'.$date->{'rfc2822'}.'</span>';\n \n \tmy $localtime_format = '(%02d:%02d %s)';\n \tif ($date->{'hour_local'} < 6) {\ndiff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js\nnew file mode 100644\nindex 0000000..c9b69c3\n--- /dev/null\n+++ b/gitweb/static/js/adjust-timezone.js\n@@ -0,0 +1,59 @@\n+// Copyright (C) 2011, John 'Warthog9' Hawley <warthog9@eaglescrag.net>\n+//               2011, Jakub Narebski <jnareb@gmail.com>\n+\n+/**\n+ * @fileOverview Manipulate dates in gitweb output, adjusting timezone\n+ * @license GPLv2 or later\n+ */\n+\n+/**\n+ * Get common timezone and adjust dates to use this common timezone.\n+ *\n+ * This function is called during onload event (added to window.onload).\n+ *\n+ * @param {String} tzDefault: default timezone, if there is no cookie\n+ * @param {String} tzCookieName: name of cookie to store timezone\n+ * @param {String} tzClassName: denotes elements with date to be adjusted\n+ */\n+function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {\n+\tvar tzCookie = getCookie(tzCookieName);\n+\tvar tz = tzCookie ? tzCookie : tzDefault;\n+\n+\t// server-side of gitweb produces datetime in UTC,\n+\t// so if tz is 'utc' there is no need for changes\n+\tif (tz !== 'utc') {\n+\t\tfixDatetimeTZ(tz, tzClassName);\n+\t}\n+}\n+\n+\n+/**\n+ * Replace RFC-2822 dates contained in SPAN elements with tzClassName\n+ * CSS class with equivalent dates in given timezone.\n+ *\n+ * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local'\n+ * @param {String} tzClassName: specifies elements to be changed\n+ */\n+function fixDatetimeTZ(tz, tzClassName) {\n+\t// sanity check, method should be ensured by common-lib.js\n+\tif (!document.getElementsByClassName) {\n+\t\treturn;\n+\t}\n+\n+\t// translate to timezone in '(-|+)HHMM' format\n+\ttz = normalizeTimezoneInfo(tz);\n+\n+\t// NOTE: result of getElementsByClassName should probably be cached\n+\tvar classesFound = document.getElementsByClassName(tzClassName, \"span\");\n+\tfor (var i = 0, len = classesFound.length; i < len; i++) {\n+\t\tvar curElement = classesFound[i];\n+\n+\t\tvar epoch = parseRFC2822Date(curElement.innerHTML);\n+\t\tvar adjusted = formatDateRFC2882(epoch, tz);\n+\n+\t\t// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2\n+\t\tcurElement.firstChild.data = adjusted;\n+\t}\n+}\n+\n+/* end of adjust-timezone.js */\ndiff --git a/gitweb/static/js/lib/datetime.js b/gitweb/static/js/lib/datetime.js\nindex b3dcedb..f78c60a 100644\n--- a/gitweb/static/js/lib/datetime.js\n+++ b/gitweb/static/js/lib/datetime.js\n@@ -105,6 +105,21 @@ function formatTimezoneInfo(hours, minutes, sep) {\n }\n \n /**\n+ * translate 'utc' and 'local' to numerical timezone\n+ * @param {String} timezoneInfo: might be 'utc' or 'local' (browser)\n+ */\n+function normalizeTimezoneInfo(timezoneInfo) {\n+\tswitch (timezoneInfo) {\n+\tcase 'utc':\n+\t\treturn '+0000';\n+\tcase 'local': // 'local' is browser timezone\n+\t\treturn localTimezoneInfo();\n+\t}\n+\treturn timezoneInfo;\n+}\n+\n+\n+/**\n  * return date in local time formatted in iso-8601 like format\n  * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200'\n  *\n-- \n1.7.3\n"},{"id":"165902","messageId":"1302878645-458-11-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv2/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:44:04Z","receivedAt":"2011-04-15T14:44:04Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\n\nThis will modify HTML, add CSS rules and add DOM event handlers so\nthat clicking on any date (the common part, not the localtime part)\nwill display a drop down menu to choose the timezone to change to.\n\nCurrently menu displays only the following timezones:\n\n  utc\n  local\n  -1200\n  -1100\n  ...\n  +1100\n  +1200\n  +1300\n  +1400\n\nIn timezone selection menu each timezone is +1hr to the previous.  The\ncode is capable of handling fractional timezones, but those have not\nbeen added to the menu.\n\nAll changes are saved to a cookie, so page changes and closing /\nreopening browser retains the last known timezone setting used.\n\n[jn: Changed from innerHTML to DOM, moved to event delegation for\nonclick to trigger menu, added close button and cookie refreshing]\n\nKNOWN BUGS:\n===========\n* Timezone selection doesn't work in Chromium 6.0.472.62 (59676) and\n  Google Chrome 10.0.648.204:\n\n  Uncaught Error: NOT_FOUND_ERR: DOM Exception 8\n    removeChangeTZForm\n    /gitweb-static/gitweb.js:785\n  onTZFormChange\n\n  line 785: var removed = container.removeChild(popup);\n\n  It works in Mozilla 1.17.2, Konqueror 3.5.3, Firefox 3.6.15, 3.6.16\n  and 4.0, Internet Explorer 8.0, Opera 10.63.\n\nHelped-by: Kevin Cernekee <cernekee@gmail.com>\nSigned-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis is an RFC mainly because of bug in timezone selection on Chrome.\nI'd have to come up with a simplified test case and hunt this bug\nfurther.\n\nDifferences from v1:\n\n* Fixed bug in gitweb.css that made style of timezone selection menu\n  not working (because of parsing errors), and required to use\n  <element>.style.cssText.\n\n  I forgot that in CSS only /* ... */ are comments, and C++-like and\n  JavaScript-like one-line // ... \"\\n\" doesn't work.\n\n* fixDatetimeTZ() didn't update timezone correctly if cookie was\n  deleted and 'utc' timezone selected from menu.\n\n  Now it is onloadTZSetup() that chooses if updating timezones is\n  needed, as only there we can assume that dates use UTC timezone\n  (that is how gitweb.perl formats it).\n\n* Fix addCssRule for IE (typofix 'stylesheets' -> 'stylesheet').\n\n  Because of browser incompatibility[1] there needs to be separate\n  branch in addCssRule for Internet Explorer... which I didn't test\n  before sending patch.  Thanks for Kevin Cernekee for reporting that\n  for IE6 (misreported as graceful degradation).\n\n  [1]: http://www.quirksmode.org/dom/w3c_css.html#change \n\n* Change code so that the fact that Opera 10.63 resets\n  target.selectedIndex to 1 after calling removeChangeTZForm()\n  doesn't affect timezone selection result.\n\n  This was fixed by simply changing the order of statements: now\n  gitweb first checks which timezone was selected, then removes\n  timezone selection menu.\n\n  Reported by Kevin Cernekee.\n\n* Moved from top+right to top+left absolute positioning (of .popup),\n  because it works correctly in wider range of [buggy] web browsers\n\n  With previous version, in 'log' view timezone menu was generated on\n  the right side of whole page (of viewport), instead of at near date,\n  as was the case with 'summary', 'commit', 'commitdiff', and 'tag'\n  views... at least for some web browsers.\n\n* removeChangeTZForm is more robust with respect to future changes.\n  It no longer assumes that `target' is parent.lastChild, but follows\n  element to be removed and its container up parentNode chain.\n\n  This was an attempt to fix Chromium / Google Chrome bug, but it\n  unfortunately didn't work.\n\n* Pass all cookie parameters from gitweb.perl, and increase cookie\n  expiration time from 7 to 14 days.\n\n  In previous version the parameter passed down the callchain was\n  tzCookieName, and expiration time and path parameters were hardcoded\n  in _two_ places.  Now what is passed iz tzCookieInfo, an object\n  literal that contains both cookie name and its parameters, including\n  expiration time / duration, and path (important when using path_info\n  URLs).\n\n* Code refactoring, putting code in a new functions to reduce size of\n  existing functions.  Namely:\n\n  - Extract/refactor generateTZOptions function \n    (this makes createChangeTZForm code shorter)\n  - select.onchange, select.onblur, close-button.onclick handlers\n    are now generated (this makes createChangeTZForm code shorter)\n\n\n gitweb/gitweb.perl                  |    3 +-\n gitweb/static/gitweb.css            |   33 ++++\n gitweb/static/js/adjust-timezone.js |  297 +++++++++++++++++++++++++++++++++--\n gitweb/static/js/lib/common-lib.js  |   27 +++-\n 4 files changed, 345 insertions(+), 15 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 6651946..b1e80ef 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3738,7 +3738,8 @@ sub git_footer_html {\n \t\t      (gitweb_check_feature('javascript-actions') ?\n \t\t      qq!\tfixLinks();\\n! : '').\n \t\t      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html\n-\t\t      qq!\tonloadTZSetup('local', 'gitweb_tz', 'datetime');\\n!.\n+\t\t      qq!\tvar tz_cookie = { name: 'gitweb_tz', expires: 14, path: '/' };\\n!. # in days\n+\t\t      qq!\tonloadTZSetup('local', tz_cookie, 'datetime');\\n!.\n \t\t      qq!};\\n!.\n \t\t      qq!</script>\\n!;\n \t}\ndiff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css\nindex 79d7eeb..8dd0935 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -579,6 +579,39 @@ div.remote {\n \tdisplay: inline-block;\n }\n \n+/* JavaScript-based timezone manipulation */\n+\n+.popup { /* timezone selection UI */\n+\tposition: absolute;\n+\t/* \"top: 0; right: 0;\" would be better, if not for bugs in browsers */\n+\ttop: 0; left: 0;\n+\tborder: 1px solid;\n+\tpadding: 2px;\n+\tbackground-color: #f0f0f0;\n+\tfont-style: normal;\n+\tcolor: #000000;\n+\tcursor: auto;\n+}\n+\n+.close-button { /* close timezone selection UI without selecting */\n+\t/* float doesn't work within absolutely positioned container,\n+\t * if width of container is not set explicitly */\n+\t/* float: right; */\n+\tposition: absolute;\n+\ttop: 0px; right: 0px;\n+\tborder:  1px solid green;\n+\tmargin:  1px 1px 1px 1px;\n+\tpadding-bottom: 2px;\n+\twidth:     12px;\n+\theight:    10px;\n+\tfont-size:  9px;\n+\tfont-weight: bold;\n+\ttext-align: center;\n+\tbackground-color: #fff0f0;\n+\tcursor: pointer;\n+}\n+\n+\n /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */\n \n /* Highlighting theme definition: */\ndiff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js\nindex c9b69c3..eec49da 100644\n--- a/gitweb/static/js/adjust-timezone.js\n+++ b/gitweb/static/js/adjust-timezone.js\n@@ -7,34 +7,51 @@\n  */\n \n /**\n- * Get common timezone and adjust dates to use this common timezone.\n+ * Get common timezone, add UI for changing timezones, and adjust\n+ * dates to use requested common timezone.\n  *\n  * This function is called during onload event (added to window.onload).\n  *\n  * @param {String} tzDefault: default timezone, if there is no cookie\n- * @param {String} tzCookieName: name of cookie to store timezone\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @param {String} tzCookieInfo.name: name of cookie to store timezone\n  * @param {String} tzClassName: denotes elements with date to be adjusted\n  */\n-function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {\n-\tvar tzCookie = getCookie(tzCookieName);\n-\tvar tz = tzCookie ? tzCookie : tzDefault;\n+function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) {\n+\tvar tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo);\n+\tvar tz = tzDefault;\n+\n+\tif (tzCookieTZ) {\n+\t\t// set timezone to value saved in a cookie\n+\t\ttz = tzCookieTZ;\n+\t\t// refresh cookie, so its expiration counts from last use of gitweb\n+\t\tsetCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo);\n+\t}\n+\n+\t// add UI for changing timezone\n+\taddChangeTZ(tz, tzCookieInfo, tzClassName);\n \n \t// server-side of gitweb produces datetime in UTC,\n \t// so if tz is 'utc' there is no need for changes\n-\tif (tz !== 'utc') {\n-\t\tfixDatetimeTZ(tz, tzClassName);\n-\t}\n+\tvar nochange = tz === 'utc';\n+\n+\t// adjust dates to use specified common timezone\n+\tfixDatetimeTZ(tz, tzClassName, nochange);\n }\n \n \n+/* ...................................................................... */\n+/* Changing dates to use requested timezone */\n+\n /**\n  * Replace RFC-2822 dates contained in SPAN elements with tzClassName\n  * CSS class with equivalent dates in given timezone.\n  *\n  * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local'\n  * @param {String} tzClassName: specifies elements to be changed\n+ * @param {Boolean} nochange: markup for timezone change, but don't change it\n  */\n-function fixDatetimeTZ(tz, tzClassName) {\n+function fixDatetimeTZ(tz, tzClassName, nochange) {\n \t// sanity check, method should be ensured by common-lib.js\n \tif (!document.getElementsByClassName) {\n \t\treturn;\n@@ -48,12 +65,266 @@ function fixDatetimeTZ(tz, tzClassName) {\n \tfor (var i = 0, len = classesFound.length; i < len; i++) {\n \t\tvar curElement = classesFound[i];\n \n-\t\tvar epoch = parseRFC2822Date(curElement.innerHTML);\n-\t\tvar adjusted = formatDateRFC2882(epoch, tz);\n+\t\tcurElement.title = 'Click to change timezone';\n+\t\tif (!nochange) {\n+\t\t\tvar epoch = parseRFC2822Date(curElement.innerHTML);\n+\t\t\tvar adjusted = formatDateRFC2882(epoch, tz);\n+\n+\t\t\t// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2\n+\t\t\tcurElement.firstChild.data = adjusted;\n+\t\t}\n+\t}\n+}\n+\n+\n+/* ...................................................................... */\n+/* Adding triggers, generating timezone menu, displaying and hiding */\n+\n+/**\n+ * Adds triggers for UI to change common timezone used for dates in\n+ * gitweb output: it marks up and/or creates item to click to invoke\n+ * timezone change UI, creates timezone UI fragment to be attached,\n+ * and installs appropriate onclick trigger (via event delegation).\n+ *\n+ * @param {String} tzSelected: pre-selected timezone,\n+ *                             'utc' or 'local' or '(-|+)HHMM'\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @param {String} tzClassName: specifies elements to install trigger\n+ */\n+function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) {\n+\t// make link to timezone UI discoverable\n+\taddCssRule('.'+tzClassName + ':hover',\n+\t           'text-decoration: underline; cursor: help;');\n+\n+\t// create form for selecting timezone (to be saved in a cookie)\n+\tvar tzSelectFragment = document.createDocumentFragment();\n+\ttzSelectFragment = createChangeTZForm(tzSelectFragment,\n+\t                                      tzSelected, tzCookieInfo, tzClassName);\n+\n+\t// event delegation handler for timezone selection UI (clicking on entry)\n+\t// see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/\n+\t// assumes that there is no existing document.onclick handler\n+\tdocument.onclick = function onclickHandler(event) {\n+\t\t//IE doesn't pass in the event object\n+\t\tevent = event || window.event;\n+\n+\t\t//IE uses srcElement as the target\n+\t\tvar target = event.target || event.srcElement;\n+\n+\t\tswitch (target.className) {\n+\t\tcase tzClassName:\n+\t\t\t// don't display timezone menu if it is already displayed\n+\t\t\tif (tzSelectFragment.childNodes.length > 0) {\n+\t\t\t\tdisplayChangeTZForm(target, tzSelectFragment);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t} // end switch\n+\t};\n+}\n+\n+/**\n+ * Create DocumentFragment with UI for changing common timezone in\n+ * which dates are shown in.\n+ *\n+ * @param {DocumentFragment} documentFragment: where attach UI\n+ * @param {String} tzSelected: default (pre-selected) timezone\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @returns {DocumentFragment}\n+ */\n+function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) {\n+\tvar div = document.createElement(\"div\");\n+\tdiv.className = 'popup';\n+\n+\t/* '<div class=\"close-button\" title=\"(click on this box to close)\">X</div>' */\n+\tvar closeButton = document.createElement('div');\n+\tcloseButton.className = 'close-button';\n+\tcloseButton.title = '(click on this box to close)';\n+\tcloseButton.appendChild(document.createTextNode('X'));\n+\tcloseButton.onclick = closeTZFormHandler(documentFragment, tzClassName);\n+\tdiv.appendChild(closeButton);\n+\n+\t/* 'Select timezone: <br clear=\"all\">' */\n+\tdiv.appendChild(document.createTextNode('Select timezone: '));\n+\tvar br = document.createElement('br');\n+\tbr.clear = 'all';\n+\tdiv.appendChild(br);\n+\n+\t/* '<select name=\"tzoffset\">\n+\t *    ...\n+\t *    <option value=\"-0700\">UTC-07:00</option>\n+\t *    <option value=\"-0600\">UTC-06:00</option>\n+\t *    ...\n+\t *  </select>' */\n+\tvar select = document.createElement(\"select\");\n+\tselect.name = \"tzoffset\";\n+\t//select.style.clear = 'all';\n+\tselect.appendChild(generateTZOptions(tzSelected));\n+\tselect.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName);\n+\t// NOTE: onblur removal might be not necessary with close button\n+\tselect.onblur = closeTZFormHandler(documentFragment, tzClassName);\n+\tdiv.appendChild(select);\n+\n+\tdocumentFragment.appendChild(div);\n+\n+\treturn documentFragment;\n+}\n+\n+\n+/**\n+ * Hide (remove from DOM) timezone change UI, ensuring that it is not\n+ * garbage collected and that it can be re-enabled later.\n+ *\n+ * @param {DocumentFragment} documentFragment: contains detached UI\n+ * @param {HTMLSelectElement} target: select element inside of UI\n+ * @param {String} tzClassName: specifies element where UI was installed\n+ * @returns {DocumentFragment} documentFragment\n+ */\n+function removeChangeTZForm(documentFragment, target, tzClassName) {\n+\t// find containing element, where we appended timezone selection UI\n+\t// `target' is somewhere inside timezone menu\n+\tvar container = target.parentNode, popup = target;\n+\twhile (container &&\n+\t       container.className !== tzClassName) {\n+\t\tpopup = container;\n+\t\tcontainer = container.parentNode;\n+\t}\n+\t// safety check if we found correct container,\n+\t// and if it isn't deleted already\n+\tif (!container || !popup ||\n+\t    container.className !== tzClassName ||\n+\t    popup.className     !== 'popup') {\n+\t\treturn documentFragment;\n+\t}\n \n-\t\t// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2\n-\t\tcurElement.firstChild.data = adjusted;\n+\t// timezone selection UI was appended as last child\n+\t// see also displayChangeTZForm function\n+\tvar removed = container.removeChild(popup);\n+\tif (documentFragment.firstChild !== removed) { // the only child\n+\t\t// re-append it so it would be available for next time\n+\t\tdocumentFragment.appendChild(removed);\n \t}\n+\t// all of inline style was added by this script\n+\tcontainer.removeAttribute('style');\n+\n+\treturn documentFragment;\n+}\n+\n+\n+/**\n+ * Display UI for changing common timezone for dates in gitweb output.\n+ * To be used from 'onclick' event handler.\n+ *\n+ * @param {HTMLElement} target: where to install/display UI\n+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI\n+ */\n+function displayChangeTZForm(target, tzSelectFragment) {\n+\t// for absolute positioning to be related to target element\n+\ttarget.style.position = 'relative';\n+\ttarget.style.display = 'inline-block';\n+\n+\t// show/display UI for changing timezone\n+\ttarget.appendChild(tzSelectFragment);\n+}\n+\n+\n+/* ...................................................................... */\n+/* List of timezones for timezone selection menu */\n+\n+/**\n+ * Generate list of timezones for creating timezone select UI\n+ *\n+ * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' }\n+ */\n+function generateTZList() {\n+\tvar timezones = [\n+\t\t{ value: \"utc\",   descr: \"UTC/GMT\"},\n+\t\t{ value: \"local\", descr: \"Local (per browser)\"}\n+\t];\n+\n+\t// generate all full hour timezones (no fractional timezones)\n+\tfor (var x = -12, idx = timezones.length; x <= +14; x++, idx++) {\n+\t\tvar hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2);\n+\t\ttimezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'};\n+\t\tif (x === 0) {\n+\t\t\ttimezones[idx].descr = 'UTC\\u00B100:00'; // 'UTC&plusmn;00:00'\n+\t\t}\n+\t}\n+\n+\treturn timezones;\n+}\n+\n+/**\n+ * Generate <options> elements for timezone select UI\n+ *\n+ * @param {String} tzSelected: default timezone\n+ * @returns {DocumentFragment} list of options elements to appendChild\n+ */\n+function generateTZOptions(tzSelected) {\n+\tvar elems = document.createDocumentFragment();\n+\tvar timezones = generateTZList();\n+\n+\tfor (var i = 0, len = timezones.length; i < len; i++) {\n+\t\tvar tzone = timezones[i];\n+\t\tvar option = document.createElement(\"option\");\n+\t\tif (tzone.value === tzSelected) {\n+\t\t\toption.defaultSelected = true;\n+\t\t}\n+\t\toption.value = tzone.value;\n+\t\toption.appendChild(document.createTextNode(tzone.descr));\n+\n+\t\telems.appendChild(option);\n+\t}\n+\n+\treturn elems;\n+}\n+\n+\n+/* ...................................................................... */\n+/* Event handlers and/or their generators */\n+\n+/**\n+ * Create event handler that select timezone and closes timezone select UI.\n+ * To be used as $('select[name=\"tzselect\"]').onchange handler.\n+ *\n+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @param {String} tzCookieInfo.name: name of cookie to save result of selection\n+ * @param {String} tzClassName: specifies element where UI was installed\n+ * @returns {Function} event handler\n+ */\n+function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) {\n+\t//return function selectTZ(event) {\n+\treturn function (event) {\n+\t\tevent = event || window.event;\n+\t\tvar target = event.target || event.srcElement;\n+\n+\t\tvar selected = target.options.item(target.selectedIndex);\n+\t\tremoveChangeTZForm(tzSelectFragment, target, tzClassName);\n+\n+\t\tif (selected) {\n+\t\t\tselected.defaultSelected = true;\n+\t\t\tsetCookie(tzCookieInfo.name, selected.value, tzCookieInfo);\n+\t\t\tfixDatetimeTZ(selected.value, tzClassName);\n+\t\t}\n+\t};\n+}\n+\n+/**\n+ * Create event handler that closes timezone select UI.\n+ * To be used e.g. as $('.closebutton').onclick handler.\n+ *\n+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI\n+ * @param {String} tzClassName: specifies element where UI was installed\n+ * @returns {Function} event handler\n+ */\n+function closeTZFormHandler(tzSelectFragment, tzClassName) {\n+\t//return function closeTZForm(event) {\n+\treturn function (event) {\n+\t\tevent = event || window.event;\n+\t\tvar target = event.target || event.srcElement;\n+\n+\t\tremoveChangeTZForm(tzSelectFragment, target, tzClassName);\n+\t};\n }\n \n /* end of adjust-timezone.js */\ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nindex b371391..018bbb7 100644\n--- a/gitweb/static/js/lib/common-lib.js\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -64,7 +64,7 @@ function padLeft(input, width, ch) {\n \n \n /* ............................................................ */\n-/* Ajax */\n+/* Handling browser incompatibilities */\n \n /**\n  * Create XMLHttpRequest object in cross-browser way\n@@ -88,6 +88,31 @@ function createRequestObject() {\n }\n \n \n+/**\n+ * Insert rule giving specified STYLE to given SELECTOR at the end of\n+ * first CSS stylesheet.\n+ *\n+ * @param {String} selector: CSS selector, e.g. '.class'\n+ * @param {String} style: rule contents, e.g. 'background-color: red;'\n+ */\n+function addCssRule(selector, style) {\n+\tvar stylesheet = document.styleSheets[0];\n+\n+\tvar theRules = [];\n+\tif (stylesheet.cssRules) {     // W3C way\n+\t\ttheRules = stylesheet.cssRules;\n+\t} else if (stylesheet.rules) { // IE way\n+\t\ttheRules = stylesheet.rules;\n+\t}\n+\n+\tif (stylesheet.insertRule) {    // W3C way\n+\t\tstylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length);\n+\t} else if (stylesheet.addRule) { // IE way\n+\t\tstylesheet.addRule(selector, style);\n+\t}\n+}\n+\n+\n /* ............................................................ */\n /* Support for legacy browsers */\n \n-- \n1.7.3\n"},{"id":"165900","messageId":"1302878645-458-12-git-send-email-jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCHv2/RFC 11/11] gitweb: Make JavaScript ability to adjust timezones configurable","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T14:44:05Z","receivedAt":"2011-04-15T14:44:05Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Configure JavaScript-based ability to select common timezone for git\ndates via %feature mechanism, namely 'javascript-timezone' feature.\n\nThe following settings are configurable:\n* default timezone (defaults to 'local' i.e. browser timezone);\n  this also can function as a way to disable this ability,\n  by setting it to false-ish value (undef or '')\n* name of cookie to store user's choice of timezone\n* class name to mark dates\n\n\nNOTE: This is a bit of abuse of %feature system, which can store only\nsequence of values, rather than dictionary (hash); usually but not\nalways only a single value is used.\n\nBased-on-code-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nBugs-reported-by: Kevin Cernekee <cernekee@gmail.com>\n\nChanges from v1:\n* Fixed bug in format_timestamp_html using \"datetime\" instead\n  of $datetime_class.\n\n gitweb/gitweb.perl |   39 +++++++++++++++++++++++++++++++--------\n 1 files changed, 31 insertions(+), 8 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex b1e80ef..ac335b6 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -480,6 +480,18 @@ our %feature = (\n \t\t'override' => 0,\n \t\t'default' => [0]},\n \n+\t# Enable and configure ability to change common timezone for dates\n+\t# in gitweb output via JavaScript.  Enabled by default.\n+\t# Project specific override is not supported.\n+\t'javascript-timezone' => {\n+\t\t'override' => 0,\n+\t\t'default' => [\n+\t\t\t'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,\n+\t\t\t             # or undef to turn off this feature\n+\t\t\t'gitweb_tz', # name of cookie where to store selected timezone\n+\t\t\t'datetime',  # CSS class used to mark up dates for manipulation\n+\t\t]},\n+\n \t# Syntax highlighting support. This is based on Daniel Svensson's\n \t# and Sham Chukoury's work in gitweb-xmms2.git.\n \t# It requires the 'highlight' program present in $PATH,\n@@ -3733,14 +3745,19 @@ sub git_footer_html {\n \t\t      qq!           \"!. href() .qq!\");\\n!.\n \t\t      qq!</script>\\n!;\n \t} else {\n+\t\tmy ($jstimezone, $tz_cookie, $datetime_class) =\n+\t\t\tgitweb_get_feature('javascript-timezone');\n+\n \t\tprint qq!<script type=\"text/javascript\">\\n!.\n-\t\t      qq!window.onload = function () {\\n!.\n-\t\t      (gitweb_check_feature('javascript-actions') ?\n-\t\t      qq!\tfixLinks();\\n! : '').\n-\t\t      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html\n-\t\t      qq!\tvar tz_cookie = { name: 'gitweb_tz', expires: 14, path: '/' };\\n!. # in days\n-\t\t      qq!\tonloadTZSetup('local', tz_cookie, 'datetime');\\n!.\n-\t\t      qq!};\\n!.\n+\t\t      qq!window.onload = function () {\\n!;\n+\t\tif (gitweb_check_feature('javascript-actions')) {\n+\t\t\tprint qq!\tfixLinks();\\n!;\n+\t\t}\n+\t\tif ($jstimezone && $tz_cookie && $datetime_class) {\n+\t\t\tprint qq!\tvar tz_cookie = { name: '$tz_cookie', expires: 14, path: '/' };\\n!. # in days\n+\t\t\t      qq!\tonloadTZSetup('$jstimezone', tz_cookie, '$datetime_class');\\n!;\n+\t\t}\n+\t\tprint qq!};\\n!.\n \t\t      qq!</script>\\n!;\n \t}\n \n@@ -3946,7 +3963,13 @@ sub git_print_section {\n \n sub format_timestamp_html {\n \tmy $date = shift;\n-\tmy $strtime = '<span class=\"datetime\">'.$date->{'rfc2822'}.'</span>';\n+\tmy $strtime = $date->{'rfc2822'};\n+\n+\tmy (undef, undef, $datetime_class) =\n+\t\tgitweb_get_feature('javascript-timezone');\n+\tif ($datetime_class) {\n+\t\t$strtime = qq!<span class=\"$datetime_class\">$strtime</span>!;\n+\t}\n \n \tmy $localtime_format = '(%02d:%02d %s)';\n \tif ($date->{'hour_local'} < 6) {\n-- \n1.7.3\n"},{"id":"165914","messageId":"201104151943.11537.jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-11-git-send-email-jnareb@gmail.com","subject":"Re: [PATCHv2/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T17:43:10Z","receivedAt":"2011-04-15T17:43:10Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Fri, 15 Apr 2011, Jakub Narebski wrote:\n\n> KNOWN BUGS:\n> ===========\n> * Timezone selection doesn't work in Chromium 6.0.472.62 (59676) and\n>   Google Chrome 10.0.648.204:\n> \n>   Uncaught Error: NOT_FOUND_ERR: DOM Exception 8\n>     removeChangeTZForm\n>     /gitweb-static/gitweb.js:785\n>   onTZFormChange\n> \n>   line 785: var removed = container.removeChild(popup);\n> \n>   It works in Mozilla 1.17.2, Konqueror 3.5.3, Firefox 3.6.15, 3.6.16\n>   and 4.0, Internet Explorer 8.0, Opera 10.63.\n\nSomething strange is happening here.  removeChangeTZForm looks like this:\n\n  function removeChangeTZForm(documentFragment, target, tzClassName) {\n\t// find containing element, where we appended timezone selection UI\n\t// `target' is somewhere inside timezone menu\n\tvar container = target.parentNode, popup = target;\n\twhile (container &&\n\t       container.className !== tzClassName) {\n\t\tpopup = container;\n\t\tcontainer = container.parentNode;\n\t}\n\t// safety check if we found correct container,\n\t// and if it isn't deleted already\n\tif (!container || !popup ||\n\t    container.className !== tzClassName ||\n\t    popup.className     !== 'popup') {\n\t\treturn documentFragment;\n\t}\n\n\t// timezone selection UI was appended as last child\n\t// see also displayChangeTZForm function\n\tvar removed = container.removeChild(popup); // <--- !!! here Chrome throws exception !!!\n\tif (documentFragment.firstChild !== removed) { // the only child\n\t\t// re-append it so it would be available for next time\n\t\tdocumentFragment.appendChild(removed);\n\t}\n\t// all of inline style was added by this script\n\tcontainer.removeAttribute('style');\n\n\treturn documentFragment;\n  }\n\nNote that from above code one would assume that after first loop we\nwould assert that we always have  popup.parentNode === contained.\nWhen working with JavaScript debugger in Google Chrome I have found\nthat at the time that exception is thrown popup.parentNode is\nDocumentFragment, not HTMLElement / HTMLSpanElement.\n\nSomething strange is going there.  I'd check if not setting 'onblur'\nevent handler (which doesn't work as expected anyway) would help.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"165918","messageId":"201104152006.27211.jnareb@gmail.com","threadId":"27104","inReplyTo":"201104151943.11537.jnareb@gmail.com","subject":"Re: [PATCHv2/RFC 10/11] gitweb.js: Add UI for selecting common timezone to display dates","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T18:06:26Z","receivedAt":"2011-04-15T18:06:26Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Jakub Narebski wrote:\n> On Fri, 15 Apr 2011, Jakub Narebski wrote:\n> \n> > KNOWN BUGS:\n> > ===========\n> > * Timezone selection doesn't work in Chromium 6.0.472.62 (59676) and\n> >   Google Chrome 10.0.648.204:\n> > \n> >   Uncaught Error: NOT_FOUND_ERR: DOM Exception 8\n> >     removeChangeTZForm\n> >     /gitweb-static/gitweb.js:785\n> >   onTZFormChange\n> > \n> >   line 785: var removed = container.removeChild(popup);\n> > \n> >   It works in Mozilla 1.17.2, Konqueror 3.5.3, Firefox 3.6.15, 3.6.16\n> >   and 4.0, Internet Explorer 8.0, Opera 10.63.\n\n> [...] I'd check if not setting 'onblur' event handler (which doesn't\n> work as expected anyway) would help. \n\nIt helps -- Google Chrome works correctly now.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"165923","messageId":"201104152117.55384.jnareb@gmail.com","threadId":"27104","inReplyTo":"201104152006.27211.jnareb@gmail.com","subject":"[PATCHv3 10/11] gitweb.js: Add UI for selecting common timezone to display dates","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-15T19:17:54Z","receivedAt":"2011-04-15T19:17:54Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"From: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\n\nThis will modify HTML, add CSS rules and add DOM event handlers so\nthat clicking on any date (the common part, not the localtime part)\nwill display a drop down menu to choose the timezone to change to.\n\nCurrently menu displays only the following timezones:\n\n  utc\n  local\n  -1200\n  -1100\n  ...\n  +1100\n  +1200\n  +1300\n  +1400\n\nIn timezone selection menu each timezone is +1hr to the previous.  The\ncode is capable of handling fractional timezones, but those have not\nbeen added to the menu.\n\nAll changes are saved to a cookie, so page changes and closing /\nreopening browser retains the last known timezone setting used.\n\n[jn: Changed from innerHTML to DOM, moved to event delegation for\nonclick to trigger menu, added close button and cookie refreshing]\n\nHelped-by: Kevin Cernekee <cernekee@gmail.com>\nSigned-off-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nInterdiff to v2 (indented with 2 spaces):\n\n  diff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js\n  index eec49da..a6e55d5 100644\n  --- a/gitweb/static/js/adjust-timezone.js\n  +++ b/gitweb/static/js/adjust-timezone.js\n  @@ -160,8 +160,6 @@ function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassN\n   \t//select.style.clear = 'all';\n   \tselect.appendChild(generateTZOptions(tzSelected));\n   \tselect.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName);\n  -\t// NOTE: onblur removal might be not necessary with close button\n  -\tselect.onblur = closeTZFormHandler(documentFragment, tzClassName);\n   \tdiv.appendChild(select);\n   \n   \tdocumentFragment.appendChild(div);\n\nThis fixes the bug that made timezone selection menu not working in\nChromium (at least in 6.0.472.62 (59676)) and Google Chrome (at least\nin 10.0.648.204) only:\n\n  Uncaught Error: NOT_FOUND_ERR: DOM Exception 8\n    removeChangeTZForm\n    /gitweb-static/gitweb.js:785\n  onTZFormChange\n\n  line 785: var removed = container.removeChild(popup);\n\n gitweb/gitweb.perl                  |    3 +-\n gitweb/static/gitweb.css            |   33 ++++\n gitweb/static/js/adjust-timezone.js |  295 +++++++++++++++++++++++++++++++++--\n gitweb/static/js/lib/common-lib.js  |   27 +++-\n 4 files changed, 343 insertions(+), 15 deletions(-)\n\ndiff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl\nindex 6651946..b1e80ef 100755\n--- a/gitweb/gitweb.perl\n+++ b/gitweb/gitweb.perl\n@@ -3738,7 +3738,8 @@ sub git_footer_html {\n \t\t      (gitweb_check_feature('javascript-actions') ?\n \t\t      qq!\tfixLinks();\\n! : '').\n \t\t      # last parameter to onloadTZSetup must be CSS class used by format_timestamp_html\n-\t\t      qq!\tonloadTZSetup('local', 'gitweb_tz', 'datetime');\\n!.\n+\t\t      qq!\tvar tz_cookie = { name: 'gitweb_tz', expires: 14, path: '/' };\\n!. # in days\n+\t\t      qq!\tonloadTZSetup('local', tz_cookie, 'datetime');\\n!.\n \t\t      qq!};\\n!.\n \t\t      qq!</script>\\n!;\n \t}\ndiff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css\nindex 79d7eeb..8dd0935 100644\n--- a/gitweb/static/gitweb.css\n+++ b/gitweb/static/gitweb.css\n@@ -579,6 +579,39 @@ div.remote {\n \tdisplay: inline-block;\n }\n \n+/* JavaScript-based timezone manipulation */\n+\n+.popup { /* timezone selection UI */\n+\tposition: absolute;\n+\t/* \"top: 0; right: 0;\" would be better, if not for bugs in browsers */\n+\ttop: 0; left: 0;\n+\tborder: 1px solid;\n+\tpadding: 2px;\n+\tbackground-color: #f0f0f0;\n+\tfont-style: normal;\n+\tcolor: #000000;\n+\tcursor: auto;\n+}\n+\n+.close-button { /* close timezone selection UI without selecting */\n+\t/* float doesn't work within absolutely positioned container,\n+\t * if width of container is not set explicitly */\n+\t/* float: right; */\n+\tposition: absolute;\n+\ttop: 0px; right: 0px;\n+\tborder:  1px solid green;\n+\tmargin:  1px 1px 1px 1px;\n+\tpadding-bottom: 2px;\n+\twidth:     12px;\n+\theight:    10px;\n+\tfont-size:  9px;\n+\tfont-weight: bold;\n+\ttext-align: center;\n+\tbackground-color: #fff0f0;\n+\tcursor: pointer;\n+}\n+\n+\n /* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */\n \n /* Highlighting theme definition: */\ndiff --git a/gitweb/static/js/adjust-timezone.js b/gitweb/static/js/adjust-timezone.js\nindex c9b69c3..a6e55d5 100644\n--- a/gitweb/static/js/adjust-timezone.js\n+++ b/gitweb/static/js/adjust-timezone.js\n@@ -7,34 +7,51 @@\n  */\n \n /**\n- * Get common timezone and adjust dates to use this common timezone.\n+ * Get common timezone, add UI for changing timezones, and adjust\n+ * dates to use requested common timezone.\n  *\n  * This function is called during onload event (added to window.onload).\n  *\n  * @param {String} tzDefault: default timezone, if there is no cookie\n- * @param {String} tzCookieName: name of cookie to store timezone\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @param {String} tzCookieInfo.name: name of cookie to store timezone\n  * @param {String} tzClassName: denotes elements with date to be adjusted\n  */\n-function onloadTZSetup(tzDefault, tzCookieName, tzClassName) {\n-\tvar tzCookie = getCookie(tzCookieName);\n-\tvar tz = tzCookie ? tzCookie : tzDefault;\n+function onloadTZSetup(tzDefault, tzCookieInfo, tzClassName) {\n+\tvar tzCookieTZ = getCookie(tzCookieInfo.name, tzCookieInfo);\n+\tvar tz = tzDefault;\n+\n+\tif (tzCookieTZ) {\n+\t\t// set timezone to value saved in a cookie\n+\t\ttz = tzCookieTZ;\n+\t\t// refresh cookie, so its expiration counts from last use of gitweb\n+\t\tsetCookie(tzCookieInfo.name, tzCookieTZ, tzCookieInfo);\n+\t}\n+\n+\t// add UI for changing timezone\n+\taddChangeTZ(tz, tzCookieInfo, tzClassName);\n \n \t// server-side of gitweb produces datetime in UTC,\n \t// so if tz is 'utc' there is no need for changes\n-\tif (tz !== 'utc') {\n-\t\tfixDatetimeTZ(tz, tzClassName);\n-\t}\n+\tvar nochange = tz === 'utc';\n+\n+\t// adjust dates to use specified common timezone\n+\tfixDatetimeTZ(tz, tzClassName, nochange);\n }\n \n \n+/* ...................................................................... */\n+/* Changing dates to use requested timezone */\n+\n /**\n  * Replace RFC-2822 dates contained in SPAN elements with tzClassName\n  * CSS class with equivalent dates in given timezone.\n  *\n  * @param {String} tz: numeric timezone in '(-|+)HHMM' format, or 'utc', or 'local'\n  * @param {String} tzClassName: specifies elements to be changed\n+ * @param {Boolean} nochange: markup for timezone change, but don't change it\n  */\n-function fixDatetimeTZ(tz, tzClassName) {\n+function fixDatetimeTZ(tz, tzClassName, nochange) {\n \t// sanity check, method should be ensured by common-lib.js\n \tif (!document.getElementsByClassName) {\n \t\treturn;\n@@ -48,12 +65,264 @@ function fixDatetimeTZ(tz, tzClassName) {\n \tfor (var i = 0, len = classesFound.length; i < len; i++) {\n \t\tvar curElement = classesFound[i];\n \n-\t\tvar epoch = parseRFC2822Date(curElement.innerHTML);\n-\t\tvar adjusted = formatDateRFC2882(epoch, tz);\n+\t\tcurElement.title = 'Click to change timezone';\n+\t\tif (!nochange) {\n+\t\t\tvar epoch = parseRFC2822Date(curElement.innerHTML);\n+\t\t\tvar adjusted = formatDateRFC2882(epoch, tz);\n+\n+\t\t\t// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2\n+\t\t\tcurElement.firstChild.data = adjusted;\n+\t\t}\n+\t}\n+}\n+\n+\n+/* ...................................................................... */\n+/* Adding triggers, generating timezone menu, displaying and hiding */\n+\n+/**\n+ * Adds triggers for UI to change common timezone used for dates in\n+ * gitweb output: it marks up and/or creates item to click to invoke\n+ * timezone change UI, creates timezone UI fragment to be attached,\n+ * and installs appropriate onclick trigger (via event delegation).\n+ *\n+ * @param {String} tzSelected: pre-selected timezone,\n+ *                             'utc' or 'local' or '(-|+)HHMM'\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @param {String} tzClassName: specifies elements to install trigger\n+ */\n+function addChangeTZ(tzSelected, tzCookieInfo, tzClassName) {\n+\t// make link to timezone UI discoverable\n+\taddCssRule('.'+tzClassName + ':hover',\n+\t           'text-decoration: underline; cursor: help;');\n+\n+\t// create form for selecting timezone (to be saved in a cookie)\n+\tvar tzSelectFragment = document.createDocumentFragment();\n+\ttzSelectFragment = createChangeTZForm(tzSelectFragment,\n+\t                                      tzSelected, tzCookieInfo, tzClassName);\n+\n+\t// event delegation handler for timezone selection UI (clicking on entry)\n+\t// see http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/\n+\t// assumes that there is no existing document.onclick handler\n+\tdocument.onclick = function onclickHandler(event) {\n+\t\t//IE doesn't pass in the event object\n+\t\tevent = event || window.event;\n+\n+\t\t//IE uses srcElement as the target\n+\t\tvar target = event.target || event.srcElement;\n+\n+\t\tswitch (target.className) {\n+\t\tcase tzClassName:\n+\t\t\t// don't display timezone menu if it is already displayed\n+\t\t\tif (tzSelectFragment.childNodes.length > 0) {\n+\t\t\t\tdisplayChangeTZForm(target, tzSelectFragment);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t} // end switch\n+\t};\n+}\n+\n+/**\n+ * Create DocumentFragment with UI for changing common timezone in\n+ * which dates are shown in.\n+ *\n+ * @param {DocumentFragment} documentFragment: where attach UI\n+ * @param {String} tzSelected: default (pre-selected) timezone\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @returns {DocumentFragment}\n+ */\n+function createChangeTZForm(documentFragment, tzSelected, tzCookieInfo, tzClassName) {\n+\tvar div = document.createElement(\"div\");\n+\tdiv.className = 'popup';\n+\n+\t/* '<div class=\"close-button\" title=\"(click on this box to close)\">X</div>' */\n+\tvar closeButton = document.createElement('div');\n+\tcloseButton.className = 'close-button';\n+\tcloseButton.title = '(click on this box to close)';\n+\tcloseButton.appendChild(document.createTextNode('X'));\n+\tcloseButton.onclick = closeTZFormHandler(documentFragment, tzClassName);\n+\tdiv.appendChild(closeButton);\n+\n+\t/* 'Select timezone: <br clear=\"all\">' */\n+\tdiv.appendChild(document.createTextNode('Select timezone: '));\n+\tvar br = document.createElement('br');\n+\tbr.clear = 'all';\n+\tdiv.appendChild(br);\n+\n+\t/* '<select name=\"tzoffset\">\n+\t *    ...\n+\t *    <option value=\"-0700\">UTC-07:00</option>\n+\t *    <option value=\"-0600\">UTC-06:00</option>\n+\t *    ...\n+\t *  </select>' */\n+\tvar select = document.createElement(\"select\");\n+\tselect.name = \"tzoffset\";\n+\t//select.style.clear = 'all';\n+\tselect.appendChild(generateTZOptions(tzSelected));\n+\tselect.onchange = selectTZHandler(documentFragment, tzCookieInfo, tzClassName);\n+\tdiv.appendChild(select);\n+\n+\tdocumentFragment.appendChild(div);\n+\n+\treturn documentFragment;\n+}\n+\n+\n+/**\n+ * Hide (remove from DOM) timezone change UI, ensuring that it is not\n+ * garbage collected and that it can be re-enabled later.\n+ *\n+ * @param {DocumentFragment} documentFragment: contains detached UI\n+ * @param {HTMLSelectElement} target: select element inside of UI\n+ * @param {String} tzClassName: specifies element where UI was installed\n+ * @returns {DocumentFragment} documentFragment\n+ */\n+function removeChangeTZForm(documentFragment, target, tzClassName) {\n+\t// find containing element, where we appended timezone selection UI\n+\t// `target' is somewhere inside timezone menu\n+\tvar container = target.parentNode, popup = target;\n+\twhile (container &&\n+\t       container.className !== tzClassName) {\n+\t\tpopup = container;\n+\t\tcontainer = container.parentNode;\n+\t}\n+\t// safety check if we found correct container,\n+\t// and if it isn't deleted already\n+\tif (!container || !popup ||\n+\t    container.className !== tzClassName ||\n+\t    popup.className     !== 'popup') {\n+\t\treturn documentFragment;\n+\t}\n \n-\t\t// curElement.innerHTML = adjusted; // does not work for Mozilla 1.17.2\n-\t\tcurElement.firstChild.data = adjusted;\n+\t// timezone selection UI was appended as last child\n+\t// see also displayChangeTZForm function\n+\tvar removed = container.removeChild(popup);\n+\tif (documentFragment.firstChild !== removed) { // the only child\n+\t\t// re-append it so it would be available for next time\n+\t\tdocumentFragment.appendChild(removed);\n \t}\n+\t// all of inline style was added by this script\n+\tcontainer.removeAttribute('style');\n+\n+\treturn documentFragment;\n+}\n+\n+\n+/**\n+ * Display UI for changing common timezone for dates in gitweb output.\n+ * To be used from 'onclick' event handler.\n+ *\n+ * @param {HTMLElement} target: where to install/display UI\n+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI\n+ */\n+function displayChangeTZForm(target, tzSelectFragment) {\n+\t// for absolute positioning to be related to target element\n+\ttarget.style.position = 'relative';\n+\ttarget.style.display = 'inline-block';\n+\n+\t// show/display UI for changing timezone\n+\ttarget.appendChild(tzSelectFragment);\n+}\n+\n+\n+/* ...................................................................... */\n+/* List of timezones for timezone selection menu */\n+\n+/**\n+ * Generate list of timezones for creating timezone select UI\n+ *\n+ * @returns {Object[]} list of e.g. { value: '+0100', descr: 'GMT+01:00' }\n+ */\n+function generateTZList() {\n+\tvar timezones = [\n+\t\t{ value: \"utc\",   descr: \"UTC/GMT\"},\n+\t\t{ value: \"local\", descr: \"Local (per browser)\"}\n+\t];\n+\n+\t// generate all full hour timezones (no fractional timezones)\n+\tfor (var x = -12, idx = timezones.length; x <= +14; x++, idx++) {\n+\t\tvar hours = (x >= 0 ? '+' : '-') + padLeft(x >=0 ? x : -x, 2);\n+\t\ttimezones[idx] = { value: hours + '00', descr: 'UTC' + hours + ':00'};\n+\t\tif (x === 0) {\n+\t\t\ttimezones[idx].descr = 'UTC\\u00B100:00'; // 'UTC&plusmn;00:00'\n+\t\t}\n+\t}\n+\n+\treturn timezones;\n+}\n+\n+/**\n+ * Generate <options> elements for timezone select UI\n+ *\n+ * @param {String} tzSelected: default timezone\n+ * @returns {DocumentFragment} list of options elements to appendChild\n+ */\n+function generateTZOptions(tzSelected) {\n+\tvar elems = document.createDocumentFragment();\n+\tvar timezones = generateTZList();\n+\n+\tfor (var i = 0, len = timezones.length; i < len; i++) {\n+\t\tvar tzone = timezones[i];\n+\t\tvar option = document.createElement(\"option\");\n+\t\tif (tzone.value === tzSelected) {\n+\t\t\toption.defaultSelected = true;\n+\t\t}\n+\t\toption.value = tzone.value;\n+\t\toption.appendChild(document.createTextNode(tzone.descr));\n+\n+\t\telems.appendChild(option);\n+\t}\n+\n+\treturn elems;\n+}\n+\n+\n+/* ...................................................................... */\n+/* Event handlers and/or their generators */\n+\n+/**\n+ * Create event handler that select timezone and closes timezone select UI.\n+ * To be used as $('select[name=\"tzselect\"]').onchange handler.\n+ *\n+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI\n+ * @param {Object} tzCookieInfo: object literal with info about cookie to store timezone\n+ * @param {String} tzCookieInfo.name: name of cookie to save result of selection\n+ * @param {String} tzClassName: specifies element where UI was installed\n+ * @returns {Function} event handler\n+ */\n+function selectTZHandler(tzSelectFragment, tzCookieInfo, tzClassName) {\n+\t//return function selectTZ(event) {\n+\treturn function (event) {\n+\t\tevent = event || window.event;\n+\t\tvar target = event.target || event.srcElement;\n+\n+\t\tvar selected = target.options.item(target.selectedIndex);\n+\t\tremoveChangeTZForm(tzSelectFragment, target, tzClassName);\n+\n+\t\tif (selected) {\n+\t\t\tselected.defaultSelected = true;\n+\t\t\tsetCookie(tzCookieInfo.name, selected.value, tzCookieInfo);\n+\t\t\tfixDatetimeTZ(selected.value, tzClassName);\n+\t\t}\n+\t};\n+}\n+\n+/**\n+ * Create event handler that closes timezone select UI.\n+ * To be used e.g. as $('.closebutton').onclick handler.\n+ *\n+ * @param {DocumentFragment} tzSelectFragment: timezone selection UI\n+ * @param {String} tzClassName: specifies element where UI was installed\n+ * @returns {Function} event handler\n+ */\n+function closeTZFormHandler(tzSelectFragment, tzClassName) {\n+\t//return function closeTZForm(event) {\n+\treturn function (event) {\n+\t\tevent = event || window.event;\n+\t\tvar target = event.target || event.srcElement;\n+\n+\t\tremoveChangeTZForm(tzSelectFragment, target, tzClassName);\n+\t};\n }\n \n /* end of adjust-timezone.js */\ndiff --git a/gitweb/static/js/lib/common-lib.js b/gitweb/static/js/lib/common-lib.js\nindex b371391..018bbb7 100644\n--- a/gitweb/static/js/lib/common-lib.js\n+++ b/gitweb/static/js/lib/common-lib.js\n@@ -64,7 +64,7 @@ function padLeft(input, width, ch) {\n \n \n /* ............................................................ */\n-/* Ajax */\n+/* Handling browser incompatibilities */\n \n /**\n  * Create XMLHttpRequest object in cross-browser way\n@@ -88,6 +88,31 @@ function createRequestObject() {\n }\n \n \n+/**\n+ * Insert rule giving specified STYLE to given SELECTOR at the end of\n+ * first CSS stylesheet.\n+ *\n+ * @param {String} selector: CSS selector, e.g. '.class'\n+ * @param {String} style: rule contents, e.g. 'background-color: red;'\n+ */\n+function addCssRule(selector, style) {\n+\tvar stylesheet = document.styleSheets[0];\n+\n+\tvar theRules = [];\n+\tif (stylesheet.cssRules) {     // W3C way\n+\t\ttheRules = stylesheet.cssRules;\n+\t} else if (stylesheet.rules) { // IE way\n+\t\ttheRules = stylesheet.rules;\n+\t}\n+\n+\tif (stylesheet.insertRule) {    // W3C way\n+\t\tstylesheet.insertRule(selector + ' { ' + style + ' }', theRules.length);\n+\t} else if (stylesheet.addRule) { // IE way\n+\t\tstylesheet.addRule(selector, style);\n+\t}\n+}\n+\n+\n /* ............................................................ */\n /* Support for legacy browsers */\n \n-- \n1.7.3\n"},{"id":"166056","messageId":"7vei4z6omh.fsf@alter.siamese.dyndns.org","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"Re: [PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-19T01:01:10Z","receivedAt":"2011-04-19T01:01:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This seems to break the build like this, by exposing a lack of dependency\nsomewhere in the Makefile:\n\n    ...\n    SUBDIR gitweb\nmake: *** No rule to make target `gitweb/static/gitweb.js', needed by `git-instaweb'.  Stop.\nmake: *** Waiting for unfinished jobs....\n"},{"id":"166057","messageId":"201104190326.29922.jnareb@gmail.com","threadId":"27104","inReplyTo":"7vei4z6omh.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-19T01:26:28Z","receivedAt":"2011-04-19T01:26:28Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> This seems to break the build like this, by exposing a lack of dependency\n> somewhere in the Makefile:\n> \n>     ...\n>     SUBDIR gitweb\n> make: *** No rule to make target `gitweb/static/gitweb.js', needed by `git-instaweb'.  Stop.\n> make: *** Waiting for unfinished jobs....\n> \n\nAh, thanks for report. and sorry for the bug.\n\nThis bug is caused by the fact that main Makefile doesn't know (as it should)\nthat gitweb.js is now generated file.\n\nThe trouble is how to write rule for gitweb/static/gitweb.js, proxying it\nto gitweb/Makefile, without having to repeat its dependencies...\n-- \nJakub Narebski\nPoland\n"},{"id":"166062","messageId":"201104190836.16221.jnareb@gmail.com","threadId":"27104","inReplyTo":"201104190326.29922.jnareb@gmail.com","subject":"Re: [PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-19T06:36:15Z","receivedAt":"2011-04-19T06:36:15Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Jakub Narebski wrote:\n> Junio C Hamano wrote:\n> \n> > This seems to break the build like this, by exposing a lack of dependency\n> > somewhere in the Makefile:\n> > \n> >     ...\n> >     SUBDIR gitweb\n> > make: *** No rule to make target `gitweb/static/gitweb.js', needed by `git-instaweb'.  Stop.\n> > make: *** Waiting for unfinished jobs....\n> > \n> \n> Ah, thanks for report. and sorry for the bug.\n> \n> This bug is caused by the fact that main Makefile doesn't know (as it should)\n> that gitweb.js is now generated file.\n\nPerhaps something like that would be a good change, what do you think?\n\n-- >8 --\ndiff --git i/Makefile w/Makefile\nindex cbc3fce..8960cee 100644\n--- i/Makefile\n+++ w/Makefile\n@@ -1773,7 +1773,7 @@ gitweb/static/gitweb.min.css: gitweb/static/gitweb.css\n endif # CSSMIN\n \n \n-git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js\n+git-instaweb: git-instaweb.sh gitweb\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n \tsed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n"},{"id":"166079","messageId":"201104191354.17923.jnareb@gmail.com","threadId":"27104","inReplyTo":"1302878645-458-1-git-send-email-jnareb@gmail.com","subject":"[PATCH -01/11] git-instaweb: Simplify build dependency on gitweb","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-19T11:54:16Z","receivedAt":"2011-04-19T11:54:16Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Since c0cb4ed (git-instaweb: Configure it to work with new gitweb\nstructure, 2010-05-28) git-instaweb does not re-create gitweb.cgi\netc., but makes use of installed gitweb.  Therefore simplify\ngit-instaweb dependency on gitweb subsystem in main Makefile from\n'gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js'\nto simply 'gitweb'.\n\nThis is preparation for splitting gitweb.perl script, and for\nsplitting gitweb.js (to be reassembled / combined on build).  This way\nwe don't have to duplicate parts of gitweb/Makefile in main\nMakefile... it is also more correct description of git-instaweb\ndependency.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\nThis is to be inserted as first patch in the series.\n\n Makefile |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex cbc3fce..8960cee 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1773,7 +1773,7 @@ gitweb/static/gitweb.min.css: gitweb/static/gitweb.css\n endif # CSSMIN\n \n \n-git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js\n+git-instaweb: git-instaweb.sh gitweb\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n \tsed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n-- \n1.7.3\n"},{"id":"166088","messageId":"7vmxjm5gae.fsf@alter.siamese.dyndns.org","threadId":"27104","inReplyTo":"201104190836.16221.jnareb@gmail.com","subject":"Re: [PATCHv2 00/11] gitweb: Change timezone in dates using JavaScript","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-19T16:58:49Z","receivedAt":"2011-04-19T16:58:49Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Perhaps something like that would be a good change, what do you think?\n\nShould be a lot saner thing to do.\n\nDo we even need to know about JSMIN, GITWEB_JS, etc., in our main\nMakefile?  Shouldn't we be delegating both the build and the install\ntargets to \"$(MAKE) -C gitweb $@\"?\n\n> diff --git i/Makefile w/Makefile\n> index cbc3fce..8960cee 100644\n> --- i/Makefile\n> +++ w/Makefile\n> @@ -1773,7 +1773,7 @@ gitweb/static/gitweb.min.css: gitweb/static/gitweb.css\n>  endif # CSSMIN\n>  \n>  \n> -git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js\n> +git-instaweb: git-instaweb.sh gitweb\n>  \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n>  \tsed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n>  \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n"},{"id":"166089","messageId":"201104191918.26909.jnareb@gmail.com","threadId":"27104","inReplyTo":"7vmxjm5gae.fsf@alter.siamese.dyndns.org","subject":"[PATCH 12/11] Remove gitweb/gitweb.cgi and other legacy targets from main Makefile","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-04-19T17:18:25Z","receivedAt":"2011-04-19T17:18:25Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Tue, 19 Apr 2011, Junio C Hamano wrote:\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n> > Perhaps something like that would be a good change, what do you think?\n> \n> Should be a lot saner thing to do.\n> \n> Do we even need to know about JSMIN, GITWEB_JS, etc., in our main\n> Makefile?  Shouldn't we be delegating both the build and the install\n> targets to \"$(MAKE) -C gitweb $@\"?\n> \n> > diff --git i/Makefile w/Makefile\n> > index cbc3fce..8960cee 100644\n> > --- i/Makefile\n> > +++ w/Makefile\n> > @@ -1773,7 +1773,7 @@ gitweb/static/gitweb.min.css: gitweb/static/gitweb.css\n> >  endif # CSSMIN\n> >  \n> >  \n> > -git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js\n> > +git-instaweb: git-instaweb.sh gitweb\n> >  \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n> >  \tsed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n> >  \t    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \\\n\nHow about that?\n\nThe patch below is to be after just sent \"git-instaweb: Simplify build\ndependency on gitweb\", not necessary as last patch in series.\n\nNote that I have left duplicated description of JSMIN and CSSMIN in main\nMakefile; perhaps they also should be removed...\n\n-- >8 --\nSubject: [PATCH 12/11] Remove gitweb/gitweb.cgi and other legacy targets from main Makefile\n\nNow that there is gitweb/Makefile, let's leave only \"gitweb\" and\n\"install-gitweb\" targets in main Makefile.  Those targets just\ndelegate to gitweb's Makefile.\n\nSigned-off-by: Jakub Narebski <jnareb@gmail.com>\n---\n Makefile |   26 --------------------------\n 1 files changed, 0 insertions(+), 26 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 8960cee..d0c577b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1747,32 +1747,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl\n gitweb:\n \t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all\n \n-ifdef JSMIN\n-GITWEB_PROGRAMS += gitweb/static/gitweb.min.js\n-GITWEB_JS = gitweb/static/gitweb.min.js\n-else\n-GITWEB_JS = gitweb/static/gitweb.js\n-endif\n-ifdef CSSMIN\n-GITWEB_PROGRAMS += gitweb/static/gitweb.min.css\n-GITWEB_CSS = gitweb/static/gitweb.min.css\n-else\n-GITWEB_CSS = gitweb/static/gitweb.css\n-endif\n-OTHER_PROGRAMS +=  gitweb/gitweb.cgi  $(GITWEB_PROGRAMS)\n-gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)\n-\t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)\n-\n-ifdef JSMIN\n-gitweb/static/gitweb.min.js: gitweb/static/gitweb.js\n-\t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)\n-endif # JSMIN\n-ifdef CSSMIN\n-gitweb/static/gitweb.min.css: gitweb/static/gitweb.css\n-\t$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)\n-endif # CSSMIN\n-\n-\n git-instaweb: git-instaweb.sh gitweb\n \t$(QUIET_GEN)$(RM) $@ $@+ && \\\n \tsed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \\\n-- \n1.7.3\n"}]}