Git fork

gitweb: Split JavaScript for maintability, combining on build

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

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

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

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

Inspired-by-patch-by: John 'Warthog9' Hawley <warthog9@eaglescrag.net>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jakub Narebski and committed by
Junio C Hamano
9a86dd57 f09f1d35

+269 -206
+1
.gitignore
··· 158 158 /gitk-git/gitk-wish 159 159 /gitweb/GITWEB-BUILD-OPTIONS 160 160 /gitweb/gitweb.cgi 161 + /gitweb/static/gitweb.js 161 162 /gitweb/static/gitweb.min.* 162 163 /test-chmtime 163 164 /test-ctype
+15 -1
gitweb/Makefile
··· 86 86 endif 87 87 endif 88 88 89 - all:: gitweb.cgi 89 + all:: gitweb.cgi static/gitweb.js 90 90 91 91 GITWEB_PROGRAMS = gitweb.cgi 92 92 ··· 112 112 113 113 GITWEB_FILES += static/git-logo.png static/git-favicon.png 114 114 115 + # JavaScript files that are composed (concatenated) to form gitweb.js 116 + # 117 + # js/lib/common-lib.js should be always first, then js/lib/*.js, 118 + # then the rest of files; js/gitweb.js should be last (if it exists) 119 + GITWEB_JSLIB_FILES += static/js/lib/common-lib.js 120 + GITWEB_JSLIB_FILES += static/js/javascript-detection.js 121 + GITWEB_JSLIB_FILES += static/js/blame_incremental.js 122 + 123 + 115 124 GITWEB_REPLACE = \ 116 125 -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \ 117 126 -e 's|++GIT_BINDIR++|$(bindir)|g' \ ··· 144 153 sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ 145 154 $(GITWEB_REPLACE) $< >$@+ && \ 146 155 chmod +x $@+ && \ 156 + mv $@+ $@ 157 + 158 + static/gitweb.js: $(GITWEB_JSLIB_FILES) 159 + $(QUIET_GEN)$(RM) $@ $@+ && \ 160 + cat $^ >$@+ && \ 147 161 mv $@+ $@ 148 162 149 163 ### Testing rules
+3 -205
gitweb/static/gitweb.js gitweb/static/js/blame_incremental.js
··· 1 1 // Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com> 2 2 // 2007, Petr Baudis <pasky@suse.cz> 3 - // 2008-2009, Jakub Narebski <jnareb@gmail.com> 3 + // 2008-2011, Jakub Narebski <jnareb@gmail.com> 4 4 5 5 /** 6 - * @fileOverview JavaScript code for gitweb (git web interface). 6 + * @fileOverview JavaScript side of Ajax-y 'blame_incremental' view in gitweb 7 7 * @license GPLv2 or later 8 8 */ 9 9 10 - /* ============================================================ */ 11 - /* functions for generic gitweb actions and views */ 12 - 13 - /** 14 - * used to check if link has 'js' query parameter already (at end), 15 - * and other reasons to not add 'js=1' param at the end of link 16 - * @constant 17 - */ 18 - var jsExceptionsRe = /[;?]js=[01]$/; 19 - 20 - /** 21 - * Add '?js=1' or ';js=1' to the end of every link in the document 22 - * that doesn't have 'js' query parameter set already. 23 - * 24 - * Links with 'js=1' lead to JavaScript version of given action, if it 25 - * exists (currently there is only 'blame_incremental' for 'blame') 26 - * 27 - * @globals jsExceptionsRe 28 - */ 29 - function fixLinks() { 30 - var allLinks = document.getElementsByTagName("a") || document.links; 31 - for (var i = 0, len = allLinks.length; i < len; i++) { 32 - var link = allLinks[i]; 33 - if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/; 34 - link.href += 35 - (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1'; 36 - } 37 - } 38 - } 39 - 40 - 41 - /* ============================================================ */ 42 10 43 11 /* 44 12 * This code uses DOM methods instead of (nonstandard) innerHTML ··· 56 24 * Note that DOM 2 HTML is preferred over generic DOM 2 Core; the 57 25 * equivalent using DOM 2 Core is usually shown in comments. 58 26 */ 59 - 60 - 61 - /* ============================================================ */ 62 - /* generic utility functions */ 63 - 64 - 65 - /** 66 - * pad number N with nonbreakable spaces on the left, to WIDTH characters 67 - * example: padLeftStr(12, 3, '\u00A0') == '\u00A012' 68 - * ('\u00A0' is nonbreakable space) 69 - * 70 - * @param {Number|String} input: number to pad 71 - * @param {Number} width: visible width of output 72 - * @param {String} str: string to prefix to string, e.g. '\u00A0' 73 - * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR 74 - */ 75 - function padLeftStr(input, width, str) { 76 - var prefix = ''; 77 - 78 - width -= input.toString().length; 79 - while (width > 0) { 80 - prefix += str; 81 - width--; 82 - } 83 - return prefix + input; 84 - } 85 - 86 - /** 87 - * Pad INPUT on the left to SIZE width, using given padding character CH, 88 - * for example padLeft('a', 3, '_') is '__a'. 89 - * 90 - * @param {String} input: input value converted to string. 91 - * @param {Number} width: desired length of output. 92 - * @param {String} ch: single character to prefix to string. 93 - * 94 - * @returns {String} Modified string, at least SIZE length. 95 - */ 96 - function padLeft(input, width, ch) { 97 - var s = input + ""; 98 - while (s.length < width) { 99 - s = ch + s; 100 - } 101 - return s; 102 - } 103 - 104 - /** 105 - * Create XMLHttpRequest object in cross-browser way 106 - * @returns XMLHttpRequest object, or null 107 - */ 108 - function createRequestObject() { 109 - try { 110 - return new XMLHttpRequest(); 111 - } catch (e) {} 112 - try { 113 - return window.createRequest(); 114 - } catch (e) {} 115 - try { 116 - return new ActiveXObject("Msxml2.XMLHTTP"); 117 - } catch (e) {} 118 - try { 119 - return new ActiveXObject("Microsoft.XMLHTTP"); 120 - } catch (e) {} 121 - 122 - return null; 123 - } 124 27 125 28 126 29 /* ============================================================ */ ··· 392 295 } 393 296 } 394 297 395 - /* ............................................................ */ 396 - /* time and data */ 397 - 398 - /** 399 - * used to extract hours and minutes from timezone info, e.g '-0900' 400 - * @constant 401 - */ 402 - var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/; 403 - 404 - /** 405 - * convert numeric timezone +/-ZZZZ to offset from UTC in seconds 406 - * 407 - * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM' 408 - * @returns {Number} offset from UTC in seconds for timezone 409 - * 410 - * @globals tzRe 411 - */ 412 - function timezoneOffset(timezoneInfo) { 413 - var match = tzRe.exec(timezoneInfo); 414 - var tz_sign = (match[1] === '-' ? -1 : +1); 415 - var tz_hour = parseInt(match[2],10); 416 - var tz_min = parseInt(match[3],10); 417 - 418 - return tz_sign*(((tz_hour*60) + tz_min)*60); 419 - } 420 - 421 - /** 422 - * return date in local time formatted in iso-8601 like format 423 - * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200' 424 - * 425 - * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC' 426 - * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM' 427 - * @returns {String} date in local time in iso-8601 like format 428 - */ 429 - function formatDateISOLocal(epoch, timezoneInfo) { 430 - // date corrected by timezone 431 - var localDate = new Date(1000 * (epoch + 432 - timezoneOffset(timezoneInfo))); 433 - var localDateStr = // e.g. '2005-08-07' 434 - localDate.getUTCFullYear() + '-' + 435 - padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' + 436 - padLeft(localDate.getUTCDate(), 2, '0'); 437 - var localTimeStr = // e.g. '21:49:46' 438 - padLeft(localDate.getUTCHours(), 2, '0') + ':' + 439 - padLeft(localDate.getUTCMinutes(), 2, '0') + ':' + 440 - padLeft(localDate.getUTCSeconds(), 2, '0'); 441 - 442 - return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo; 443 - } 444 - 445 - /* ............................................................ */ 446 - /* unquoting/unescaping filenames */ 447 - 448 - /**#@+ 449 - * @constant 450 - */ 451 - var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g; 452 - var octEscRe = /^[0-7]{1,3}$/; 453 - var maybeQuotedRe = /^\"(.*)\"$/; 454 - /**#@-*/ 455 - 456 - /** 457 - * unquote maybe git-quoted filename 458 - * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a a' 459 - * 460 - * @param {String} str: git-quoted string 461 - * @returns {String} Unquoted and unescaped string 462 - * 463 - * @globals escCodeRe, octEscRe, maybeQuotedRe 464 - */ 465 - function unquote(str) { 466 - function unq(seq) { 467 - var es = { 468 - // character escape codes, aka escape sequences (from C) 469 - // replacements are to some extent JavaScript specific 470 - t: "\t", // tab (HT, TAB) 471 - n: "\n", // newline (NL) 472 - r: "\r", // return (CR) 473 - f: "\f", // form feed (FF) 474 - b: "\b", // backspace (BS) 475 - a: "\x07", // alarm (bell) (BEL) 476 - e: "\x1B", // escape (ESC) 477 - v: "\v" // vertical tab (VT) 478 - }; 479 - 480 - if (seq.search(octEscRe) !== -1) { 481 - // octal char sequence 482 - return String.fromCharCode(parseInt(seq, 8)); 483 - } else if (seq in es) { 484 - // C escape sequence, aka character escape code 485 - return es[seq]; 486 - } 487 - // quoted ordinary character 488 - return seq; 489 - } 490 - 491 - var match = str.match(maybeQuotedRe); 492 - if (match) { 493 - str = match[1]; 494 - // perhaps str = eval('"'+str+'"'); would be enough? 495 - str = str.replace(escCodeRe, 496 - function (substr, p1, offset, s) { return unq(p1); }); 497 - } 498 - return str; 499 - } 500 298 501 299 /* ============================================================ */ 502 300 /* main part: parsing response */ ··· 886 684 pollTimer = setInterval(xhr.onreadystatechange, 1000); 887 685 } 888 686 889 - // end of gitweb.js 687 + /* end of blame_incremental.js */
+20
gitweb/static/js/README
··· 1 + GIT web interface (gitweb) - JavaScript 2 + ======================================= 3 + 4 + This directory holds JavaScript code used by gitweb (GIT web interface). 5 + Scripts from there would be concatenated together in the order specified 6 + by gitweb/Makefile into gitweb/static/gitweb.js, during building of 7 + gitweb/gitweb.cgi (during gitweb building). The resulting file (or its 8 + minification) would then be installed / deployed together with gitweb. 9 + 10 + Scripts in 'lib/' subdirectory compose generic JavaScript library, 11 + providing features required by gitweb but in no way limited to gitweb 12 + only. In the future those scripts could be replaced by some JavaScript 13 + library / framework, like e.g. jQuery, YUI, Prototype, MooTools, Dojo, 14 + ExtJS, Script.aculo.us or SproutCore. 15 + 16 + All scripts that manipulate gitweb output should be put outside 'lib/', 17 + directly in this directory ('gitweb/static/js/'). Those scripts would 18 + have to be rewritten if gitweb moves to using some JavaScript library. 19 + 20 + See also comments in gitweb/Makefile.
+43
gitweb/static/js/javascript-detection.js
··· 1 + // Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com> 2 + // 2007, Petr Baudis <pasky@suse.cz> 3 + // 2008-2011, Jakub Narebski <jnareb@gmail.com> 4 + 5 + /** 6 + * @fileOverview Detect if JavaScript is enabled, and pass it to server-side 7 + * @license GPLv2 or later 8 + */ 9 + 10 + 11 + /* ============================================================ */ 12 + /* Manipulating links */ 13 + 14 + /** 15 + * used to check if link has 'js' query parameter already (at end), 16 + * and other reasons to not add 'js=1' param at the end of link 17 + * @constant 18 + */ 19 + var jsExceptionsRe = /[;?]js=[01]$/; 20 + 21 + /** 22 + * Add '?js=1' or ';js=1' to the end of every link in the document 23 + * that doesn't have 'js' query parameter set already. 24 + * 25 + * Links with 'js=1' lead to JavaScript version of given action, if it 26 + * exists (currently there is only 'blame_incremental' for 'blame') 27 + * 28 + * To be used as `window.onload` handler 29 + * 30 + * @globals jsExceptionsRe 31 + */ 32 + function fixLinks() { 33 + var allLinks = document.getElementsByTagName("a") || document.links; 34 + for (var i = 0, len = allLinks.length; i < len; i++) { 35 + var link = allLinks[i]; 36 + if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/; 37 + link.href += 38 + (link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1'; 39 + } 40 + } 41 + } 42 + 43 + /* end of javascript-detection.js */
+187
gitweb/static/js/lib/common-lib.js
··· 1 + // Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com> 2 + // 2007, Petr Baudis <pasky@suse.cz> 3 + // 2008-2011, Jakub Narebski <jnareb@gmail.com> 4 + 5 + /** 6 + * @fileOverview Generic JavaScript code (helper functions) 7 + * @license GPLv2 or later 8 + */ 9 + 10 + 11 + /* ============================================================ */ 12 + /* ............................................................ */ 13 + /* Padding */ 14 + 15 + /** 16 + * pad number N with nonbreakable spaces on the left, to WIDTH characters 17 + * example: padLeftStr(12, 3, '\u00A0') == '\u00A012' 18 + * ('\u00A0' is nonbreakable space) 19 + * 20 + * @param {Number|String} input: number to pad 21 + * @param {Number} width: visible width of output 22 + * @param {String} str: string to prefix to string, e.g. '\u00A0' 23 + * @returns {String} INPUT prefixed with (WIDTH - INPUT.length) x STR 24 + */ 25 + function padLeftStr(input, width, str) { 26 + var prefix = ''; 27 + 28 + width -= input.toString().length; 29 + while (width > 0) { 30 + prefix += str; 31 + width--; 32 + } 33 + return prefix + input; 34 + } 35 + 36 + /** 37 + * Pad INPUT on the left to SIZE width, using given padding character CH, 38 + * for example padLeft('a', 3, '_') is '__a'. 39 + * 40 + * @param {String} input: input value converted to string. 41 + * @param {Number} width: desired length of output. 42 + * @param {String} ch: single character to prefix to string. 43 + * 44 + * @returns {String} Modified string, at least SIZE length. 45 + */ 46 + function padLeft(input, width, ch) { 47 + var s = input + ""; 48 + while (s.length < width) { 49 + s = ch + s; 50 + } 51 + return s; 52 + } 53 + 54 + 55 + /* ............................................................ */ 56 + /* Ajax */ 57 + 58 + /** 59 + * Create XMLHttpRequest object in cross-browser way 60 + * @returns XMLHttpRequest object, or null 61 + */ 62 + function createRequestObject() { 63 + try { 64 + return new XMLHttpRequest(); 65 + } catch (e) {} 66 + try { 67 + return window.createRequest(); 68 + } catch (e) {} 69 + try { 70 + return new ActiveXObject("Msxml2.XMLHTTP"); 71 + } catch (e) {} 72 + try { 73 + return new ActiveXObject("Microsoft.XMLHTTP"); 74 + } catch (e) {} 75 + 76 + return null; 77 + } 78 + 79 + 80 + /* ............................................................ */ 81 + /* time and data */ 82 + 83 + /** 84 + * used to extract hours and minutes from timezone info, e.g '-0900' 85 + * @constant 86 + */ 87 + var tzRe = /^([+-])([0-9][0-9])([0-9][0-9])$/; 88 + 89 + /** 90 + * convert numeric timezone +/-ZZZZ to offset from UTC in seconds 91 + * 92 + * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM' 93 + * @returns {Number} offset from UTC in seconds for timezone 94 + * 95 + * @globals tzRe 96 + */ 97 + function timezoneOffset(timezoneInfo) { 98 + var match = tzRe.exec(timezoneInfo); 99 + var tz_sign = (match[1] === '-' ? -1 : +1); 100 + var tz_hour = parseInt(match[2],10); 101 + var tz_min = parseInt(match[3],10); 102 + 103 + return tz_sign*(((tz_hour*60) + tz_min)*60); 104 + } 105 + 106 + /** 107 + * return date in local time formatted in iso-8601 like format 108 + * 'yyyy-mm-dd HH:MM:SS +/-ZZZZ' e.g. '2005-08-07 21:49:46 +0200' 109 + * 110 + * @param {Number} epoch: seconds since '00:00:00 1970-01-01 UTC' 111 + * @param {String} timezoneInfo: numeric timezone '(+|-)HHMM' 112 + * @returns {String} date in local time in iso-8601 like format 113 + */ 114 + function formatDateISOLocal(epoch, timezoneInfo) { 115 + // date corrected by timezone 116 + var localDate = new Date(1000 * (epoch + 117 + timezoneOffset(timezoneInfo))); 118 + var localDateStr = // e.g. '2005-08-07' 119 + localDate.getUTCFullYear() + '-' + 120 + padLeft(localDate.getUTCMonth()+1, 2, '0') + '-' + 121 + padLeft(localDate.getUTCDate(), 2, '0'); 122 + var localTimeStr = // e.g. '21:49:46' 123 + padLeft(localDate.getUTCHours(), 2, '0') + ':' + 124 + padLeft(localDate.getUTCMinutes(), 2, '0') + ':' + 125 + padLeft(localDate.getUTCSeconds(), 2, '0'); 126 + 127 + return localDateStr + ' ' + localTimeStr + ' ' + timezoneInfo; 128 + } 129 + 130 + 131 + /* ............................................................ */ 132 + /* unquoting/unescaping filenames */ 133 + 134 + /**#@+ 135 + * @constant 136 + */ 137 + var escCodeRe = /\\([^0-7]|[0-7]{1,3})/g; 138 + var octEscRe = /^[0-7]{1,3}$/; 139 + var maybeQuotedRe = /^\"(.*)\"$/; 140 + /**#@-*/ 141 + 142 + /** 143 + * unquote maybe git-quoted filename 144 + * e.g. 'aa' -> 'aa', '"a\ta"' -> 'a a' 145 + * 146 + * @param {String} str: git-quoted string 147 + * @returns {String} Unquoted and unescaped string 148 + * 149 + * @globals escCodeRe, octEscRe, maybeQuotedRe 150 + */ 151 + function unquote(str) { 152 + function unq(seq) { 153 + var es = { 154 + // character escape codes, aka escape sequences (from C) 155 + // replacements are to some extent JavaScript specific 156 + t: "\t", // tab (HT, TAB) 157 + n: "\n", // newline (NL) 158 + r: "\r", // return (CR) 159 + f: "\f", // form feed (FF) 160 + b: "\b", // backspace (BS) 161 + a: "\x07", // alarm (bell) (BEL) 162 + e: "\x1B", // escape (ESC) 163 + v: "\v" // vertical tab (VT) 164 + }; 165 + 166 + if (seq.search(octEscRe) !== -1) { 167 + // octal char sequence 168 + return String.fromCharCode(parseInt(seq, 8)); 169 + } else if (seq in es) { 170 + // C escape sequence, aka character escape code 171 + return es[seq]; 172 + } 173 + // quoted ordinary character 174 + return seq; 175 + } 176 + 177 + var match = str.match(maybeQuotedRe); 178 + if (match) { 179 + str = match[1]; 180 + // perhaps str = eval('"'+str+'"'); would be enough? 181 + str = str.replace(escCodeRe, 182 + function (substr, p1, offset, s) { return unq(p1); }); 183 + } 184 + return str; 185 + } 186 + 187 + /* end of common-lib.js */