9cd2a0c79be8da1eca5b27f9a045750ebe0da90c
[sjy/austlii.git] / austlii.js
1 /* austlii.js
2  * Author: Scott Young <splintax@ucc.asn.au>
3  * Bookmarklet to add annotation tools to AustLII cases. Invoke with:
4  * javascript:var d=document;var s=d.createElement("script");s.src="http://splintax.ucc.asn.au/austlii/austlii.js";s.type="text/javascript";d.getElementsByTagName('head').item(0).appendChild(s); */
5
6 // create a namespace for this tool
7 window.austlii = {
8     load: function(url, cb) {
9         // Load an arbitrary JavaScript file and fire cb.
10         var s = document.createElement("script");
11         s.src = url; s.type = "text/javascript"; s.onload = cb;
12         document.getElementsByTagName('head').item(0).appendChild(s); return;
13     },
14     style: function(url) {
15         // dynamically add a new stylesheet
16         var s = document.createElement("link");
17         s.rel = "stylesheet"; s.type = "text/css"; s.href = url; 
18         document.getElementsByTagName('head').item(0).appendChild(s); return;
19     },
20     url: function(relative_path) {
21         // return url relative to the source of this file, eg. http://splintax.ucc.asn.au/austlii/
22         var prefix = $('script[src$="austlii.js"]').attr('src').replace(/austlii\.js/, "");
23         return prefix + relative_path;
24     }
25 };
26
27 // Initialises once jQuery is loaded.
28 austlii.main = function() {
29     austlii.style(austlii.url("austlii.css")); // inject css
30     // set viewport width so iPad doesn't zoom out
31     $("head").append('<meta name="viewport" content="initial-scale=1.0" />');
32     $("a").slice(10,16).css({"margin-right": "1em"}); // space out "Database Search", ... links
33
34     austlii.markup();
35     austlii.menu();
36     $(window).scroll(austlii.scrollHandler).scroll();
37
38     // enable smooth scrolling on anchors
39     // TODO: doing this synchronously makes for slow injection on mobile browsers
40     austlii.load(austlii.url("jquery.scrollTo-1.4.2-min.js"), function() {
41         austlii.load(austlii.url("jquery.localscroll-1.2.7-min.js"), function() {
42             $.localScroll({duration: '200'});
43         });
44     });
45
46     // TODO: user annotations
47     $("#trigger-highlight").click(austlii.highlight);
48 }
49
50 // Cleans up page and adds semantic markup.
51 austlii.markup = function() {
52     // remove cruft
53     $("br").remove();
54     $('img[alt="AustLII"]').detach().prependTo("body");
55     $("table").remove();
56
57     $("h1").wrap('<a class="section" name="top">'); // add back-to-top
58     $('a[name="fn1"]').addClass("section"); // mark beginning of footnotes
59
60     // find and markup judges
61     var judgeExp = /([A-Zc]{3})((?:[A-Z\s,]|&nbsp;)+C?J?J\.)/g
62     /* This regex is confusing and I keep breaking it. Remember:
63      * [A-Zc] is used to match McHUGH J.
64      * Since we're matching innerHTML &nbsp; is not part of \s.
65      * The first 3 characters are captured, hopefully because they are ASCII non-whitespace
66      * suitable for use in the <a name>. */
67     var replacementText = document.body.innerHTML.replace(judgeExp, '<a class="section" name="$1">$1$2</a>');
68     document.body.innerHTML = replacementText;
69 };
70
71 // Updates responsive menu whenever the page is scrolled.
72 austlii.scrollHandler = function() {
73     var currentPosition = $(window).scrollTop() + $(window).height()/2;
74     
75     var $anchors = $("#container a.section");
76     var $links = $("#menu ol li a");
77     var $pars = $("#container ol li").not("ol ol li");
78
79     // remove existing section highlight and citation
80     $links.removeClass("current");
81     $("#menu .pinpoint").remove();
82
83     // find the current document section
84     var $anchor = $anchors.filter(function(){
85         // is this anchor above the fold?
86         return currentPosition > $(this).offset().top;
87     }).last(); // current section = last anchor above fold
88     // highlight the link to the current document section
89     $links.filter('[href="#' + $anchor.attr("name") + '"]').addClass("current");
90
91     // check whether we're in the judgment body
92     if ( currentPosition > $pars.first().offset().top &&
93          currentPosition < $pars.last().offset().top ) {
94         // we are, so find the current paragraph and add a pinpoint citation
95         var $par = $pars.filter(function(){
96             // is this paragraph above the fold?
97             return currentPosition > $(this).offset().top;
98         }).last(); // current paragraph = last paragraph above fold
99         if ( $par.val() != 0 ) { // don't add unofficial paragraph citations on old cases
100             // TODO: make it work anyway, but add a warning?
101             var $pinpoint = $('<span class="pinpoint"> at ['+ $par.val() +']</span>');
102             $("#menu a.current").after($pinpoint); // add citation
103         }
104     }
105 };
106
107 // Create the responsive menu.
108 austlii.menu = function() {
109     // wrap the source body content in a container
110     $("body").children().wrapAll('<div id="container" />');
111
112     // create and prepend menu to the page
113     var $menu = $('<div id="menu"></div>');
114     $('<h2>'+ $("h2").first().html() +'</h2>').prependTo($menu); // add header
115     $('img[alt="AustLII"]').detach().prependTo($menu);
116     $menu.prependTo("body");
117
118     // create and insert links in the menu
119     var $links = $('<ol />');
120     // headnote
121     $links.append('<li><a href="#top">Headnote</a><span class="spacer">&middot;</span></li>')
122     // judgments
123     var $judges = $("a.section").slice(1,-1);
124     $judges.each(function(i){
125         var li = '<li><a href="#' + this.name + '">' + $(this).html().slice(0,-1) + '</a>';
126         if ( i < $judges.length )
127             li += '<span class="spacer">&middot;</span>'
128         li += '</li>';
129         $links.append(li);
130     });
131     // footnotes
132     $links.append('<li><a href="#fn1">Footnotes</a></li>');
133     $menu.append($links);
134
135     // add annotation features
136     //$menu.append($('<a id="trigger-highlight" href="#">Highlight</a>'));
137 };
138
139 // FIXME Highlight the current selection.
140 austlii.highlight = function(event) {
141     event.preventDefault();
142     var sel = document.getSelection();
143     if (sel.type == "Range") {
144         // add highlight
145         var range = sel.getRangeAt(0);
146         var $note = $('<span style="background-color: yellow;" />');
147         range.surroundContents($note[0]);
148
149         console.log(sel);
150         var $menu = $('<div style="position: absolute;" />');
151         var $removeLink = $('<a href="#">Remove</a>');
152         $note.append($menu);
153         $menu.append($removeLink);
154         $removeLink.click((function($hilite) {
155             var text = $hilite.text();
156             $hilite.after(text).remove();
157         })($(this)));
158     }
159     else {console.log("You haven't highlighted anything.")}
160 };
161
162 // don't run on other sites
163 if (window.location.host.slice(-14) != "austlii.edu.au" ||
164     window.location.pathname.slice(0,10) != "/au/cases/" ||
165     window.location.pathname.slice(-5) != ".html")
166     console.error("You're not looking at a case on AustLII.");
167
168 else if (document.getElementsByTagName("script").length > 1)
169     console.error("austlii.js will not run when there are other scripts on the page.");
170
171 else {
172     console.log("Loaded austlii.js.");
173     // load jQuery and initialise the bookmarklet
174     austlii.load('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', austlii.main);
175 }

UCC git Repository :: git.ucc.asn.au