git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCHv3/RFC 4/5] gitweb: Create links leading to 'blame_incremental' using JavaScript

From
Jakub Narebski <jnareb@gmail.com>
Date
Sep 1, 2009, 11:39 UTC
Message-ID
<1251805160-5303-5-git-send-email-jnareb@gmail.com>
In-Reply-To
<1251805160-5303-4-git-send-email-jnareb@gmail.com>

The new 'blame_incremental' view requires JavaScript to run. Not all web browsers implement JavaScript (e.g. text browsers such as Lynx), and not all users have JavaScript enabled. Therefore instead of unconditionally linking to 'blame_incremental' view, we use JavaScript to convert those links to lead to view utilizing JavaScript, by adding 'js=1' to link.

Currently the only action that takes 'js=1' into account is 'blame', which then acts as if it was called as 'blame_incremental' action. Possible enhancement would be to do JavaScript redirect by setting window.location instead of modifying $format and $action in git_blame_common() subroutine.

The only JavaScript-aware/using view is currently 'blame_incremental'. While at it move reading JavaScript to git_footer_html() subroutine. Note that in this view we do not add 'js=1' currently (even though perhaps we should; note that for consistency we should also add 'js=1' in links added by JavaScript part of 'blame_incremental').

This idea was originally implemented by Petr Baudis in
  http://article.gmane.org/gmane.comp.version-control.git/47614
but it added <script> element with fixBlameLinks() function in page
header, to be added as onload event using 'onload' attribute of HTML
'body' element: <body onload="fixBlameLinks();">.  This version adds
script at then end of page (in the page footer), and uses JavaScript
'window.onload=fixLinks();'.  Also in Petr version only links marked
with 'blamelink' class were modified, and they were modified by
replacing "a=blame" by "a=blame_incremental"... which doesn't work for
path_info links, and might replace wrong part if there is "a=blame" in
project name, ref name or file name.
Slightly different solution was implemented by Martin Koegler in
  http://thread.gmane.org/gmane.comp.version-control.git/47902/focus=47905
Here GitAddLinks() function was in gitweb.js file, not as contents of
<script> element.  It was also included in page header (in <head>
element) though, which means waiting for a script to load (and run).
It was smarter in that to "fix" (modify) link, it split URL, modified
value of 'a' parameter, and then recreated modified link.  It avoids
trouble with "a=blame" as substring in project name or file name, but
it doesn't work with path_info URL/link in the way it was written.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This patch is in RFC stage.

Currently fixLinks() is not invoked for 'blame_incremental', but links generated by startBlame (or to be more exact by event handler in gitweb.js) lead to 'blame_incremental' directly. Also the list of "JavaScript-aware actions" is currently hardcoded to 'blame_incremental' (and relies on 'blame' + 'js=1' to change $action).

Also fixLinks() adds 'js=1' to too many links, including $home_link, and links to feeds (where JavaScript is not supported anyway). Alternate solution would be to mark links which lead to action which has JavaScript-requiring alternative, either marking it via 'class' attribute, or providing for example 'alt_href' or 'jshref' attribute with link to JavaScript-requiring variant.

Differences from previous version (v2):
* fixLinks() function is put in gitweb.js together with all JavaScript
  code required for 'blame_incremental' view, instead of being defined
  inside <script> element.
* Running startBlame() was moved to git_footer_html, instead of being
  in git_blame_common
* The link to plain 'blame' view (which do not require JavaScript
  support enabled in web browser) inside <noscript> element is marked
  with 'js=0'.
* fixLinks() now doesn't add 'js=1' if links ends with '[?;]js=[01]'
  (so that e.g. 'js=0' means link to view which do not require
  JavaScript).
* performance: calculate number of elements in a list before loop
TODO for future commits:
* Use 'click' event to change links to jave 'js=1' parameter appended;
  this way we would check if JavaScript is enabled at the moment of
  following (clicking) link, not at the moment of loading the page.
  Unfortunately adding event listeners (much better solution than
  providing/adding 'onclick' attribute) is different in different
  browsers.  Some kind of wrapper around browser incompatibilities
  would be required.
 gitweb/gitweb.js   |   34 ++++++++++++++++++++++++++++++++++
 gitweb/gitweb.perl |   28 +++++++++++++++++++---------
 2 files changed, 53 insertions(+), 9 deletions(-)
diff --git a/gitweb/gitweb.js b/gitweb/gitweb.js
index bf38216..66fd64e 100644
--- a/gitweb/gitweb.js
+++ b/gitweb/gitweb.js
@@ -7,6 +7,39 @@
  * @license GPLv2 or later
  */
 
+/* ============================================================ */
+/* functions for generic gitweb actions and views */
+
+/**
+ * used to check if link has 'js' query parameter already (at end),
+ * and other reasons to not add 'js=1' param at the end of link
+ * @constant
+ */
+var jsExceptionsRe = /[;?]js=[01]$/;
+
+/**
+ * Add '?js=1' or ';js=1' to the end of every link in the document
+ * that doesn't have 'js' query parameter set already.
+ *
+ * Links with 'js=1' lead to JavaScript version of given action, if it
+ * exists (currently there is only 'blame_incremental' for 'blame')
+ *
+ * @globals jsExceptionsRe
+ */
+function fixLinks() {
+	var allLinks = document.getElementsByTagName("a") || document.links;
+	for (var i = 0, len = allLinks.length; i < len; i++) {
+		var link = allLinks[i];
+		if (!jsExceptionsRe.test(link)) { // =~ /[;?]js=[01]$/;
+			link.href +=
+				(link.href.indexOf('?') === -1 ? '?' : ';') + 'js=1';
+		}
+	}
+}
+
+
+/* ============================================================ */
+
 /*
  * This code uses DOM methods instead of (nonstandard) innerHTML
  * to modify page.
@@ -89,6 +122,7 @@ function createRequestObject() {
 	return null;
 }
 
+
 /* ============================================================ */
 /* utility/helper functions (and variables) */
 
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 88c91ff..0adfd3f 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -571,6 +571,8 @@ our @cgi_param_mapping = (
 	snapshot_format => "sf",
 	extra_options => "opt",
 	search_use_regexp => "sr",
+	# this must be last entry (for manipulation from JavaScript)
+	javascript => "js"
 );
 our %cgi_param_mapping = @cgi_param_mapping;
 
@@ -3255,6 +3257,18 @@ sub git_footer_html {
 		insert_file($site_footer);
 	}
 
+	print qq!<script type="text/javascript" src="$javascript"></script>\n!;
+	if ($action eq 'blame_incremental') {
+		print qq!<script type="text/javascript">\n!.
+		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
+		      qq!           "!. href() .qq!");\n!.
+		      qq!</script>\n!;
+	} else {
+		print qq!<script type="text/javascript">\n!.
+		      qq!window.onload = fixLinks;\n!.
+		      qq!</script>\n!;
+	}
+
 	print "</body>\n" .
 	      "</html>";
 }
@@ -4806,6 +4820,10 @@ sub git_tag {
 
 sub git_blame_common {
 	my $format = shift || 'porcelain';
+	if ($format eq 'porcelain' && $cgi->param('js')) {
+		$format = 'incremental';
+		$action = 'blame_incremental'; # for page title etc
+	}
 
 	# permissions
 	gitweb_check_feature('blame')
@@ -4885,7 +4903,7 @@ sub git_blame_common {
 	if ($format eq 'incremental') {
 		print "<noscript>\n<div class=\"error\"><center><b>\n".
 		      "This page requires JavaScript to run.\n Use ".
-		      $cgi->a({-href => href(action=>'blame',-replay=>1)},
+		      $cgi->a({-href => href(action=>'blame',javascript=>0,-replay=>1)},
 		              'this page').
 		      " instead.\n".
 		      "</b></center></div>\n</noscript>\n";
@@ -5016,14 +5034,6 @@ sub git_blame_common {
 	close $fd
 		or print "Reading blob failed\n";
 
-	if ($format eq 'incremental') {
-		print qq!<script type="text/javascript" src="$javascript"></script>\n!.
-		      qq!<script type="text/javascript">\n!.
-		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
-		      qq!           "!. href() .qq!");\n!.
-		      qq!</script>\n!;
-	}
-
 	git_footer_html();
 }
 
-- 
1.6.3.3
Previous: Jakub NarebskiNext: Jakub Narebski
Message 5 of 12 in “gitweb: Incremental blame series (1 Sep 09)”
  1. 0/5 gitweb: Incremental blame series (1 Sep 09)Jakub Narebski, Sep 1, 2009
  2. 1/5 gitweb: Add optional "time to generate page" info in footerJakub Narebski, Sep 1, 2009
  3. 2/5 gitweb: Incremental blame (using JavaScript)Jakub Narebski, Sep 1, 2009
  4. 3/5 gitweb: Colorize 'blame_incremental' view during processingJakub Narebski, Sep 1, 2009
  5. 4/5 gitweb: Create links leading to 'blame_incremental' using JavaScriptJakub Narebski, Sep 1, 2009
  6. 5/5 gitweb: Minify gitweb.js if JSMIN is definedJakub Narebski, Sep 1, 2009
  7. Petr BaudisNov 5, 2009
  8. Jakub NarebskiNov 6, 2009
  9. Junio C HamanoNov 12, 2009
  10. Jakub NarebskiNov 12, 2009
  11. Petr BaudisNov 5, 2009
  12. Jakub NarebskiNov 7, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.