PageSourceSearch

https://www.legislation.qld.gov.au/js/website-commons.local-revision.js

js legislation.qld.gov.au collected 2026-10-02 18:15:33 UTC 123,545 bytes, 3,454 lines download raw bytes

1/**
2 * Utils section.
3 * Contain helper functions to build ccl queries and other
4 * cross platform support functions
5 */
6$.fn.serializeObject = function() {
7    var o = {};
8    var a = this.serializeArray();
9    $.each(a, function() {
10        if (o[this.name] !== undefined) {
11            if (!o[this.name].push) {
12                o[this.name] = [o[this.name]];
13            }
14            o[this.name].push(this.value || '');
15        } else {
16            o[this.name] = this.value || '';
17        }
18    });
19    return o;
20};
21
22String.prototype.cclTermSafe = function() {
23    return ['"', this.replace(/"/g, ""), '"'].join('');
24};
25
26String.prototype.wrapWithBrackets = function() {
27    return ['(', this, ')'].join('');
28};
29
30Array.prototype.cclTermSafe = function() {
31    var newArray = [];
32    $.each(this, function(index, value) {
33        newArray.push(['"', value.replace(/"/g, ""), '"'].join(''));
34    });
35
36    return newArray;
37};
38
39// Returns a JSON object with all URL parameters
40function getURLParameters(url) {
41    var resultJson = {};
42    if (url && url.length > 0) {
43        var splitParams = url.substring(1).split("&");
44        for (var i = 0; i < splitParams.length; i++) {
45            var splitParam = splitParams[i].split("=");
46            if (splitParam.length === 2) {
47                var paramName = decodeURIComponent(splitParam[0]);
48                var paramVal = decodeURIComponent(splitParam[1].replace(/\+/g, " "));
49                var currentVal = resultJson[paramName];
50                if (currentVal) {
51                    if ($.isArray(currentVal)) {
52                        currentVal.push(paramVal);
53                        resultJson[paramName] = currentVal;
54                    } else {
55                        resultJson[paramName] = [currentVal, paramVal];
56                    }
57                } else {
58                    resultJson[paramName] = paramVal;
59                }
60            }
61        }
62    }
63    return resultJson;
64}
65
66function getURLParameter(name) {
67    
68    var uri = decodeURIComponent((RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]);
69    if(uri)
70        uri = uri.replace(/[+]/g, " ");
71        
72    return uri;
73}
74
75function isValidDate(dateString) {
76    if(dateString == null || dateString == "") {
77        return false;
78    }
79    return dateString.search(/1500-01-01/) == -1;
80}
81
82function secondsToMidnight() {
83    var now = new Date();
84    var then = new Date(now)
85    then.setHours(24,0,0,0);
86    // 1 minute = 6e4 milliseconds
87    return (then - now) / 60
88}
89
90String.prototype.hashCode = function() {
91    var hash = 0;
92    if (this.length == 0) return hash;
93    for (i=0; i < this.length; i++) {
94        c = this.charCodeAt(i);
95        hash = ((hash<<5) - hash) + c;
96        hash = hash & hash; // Convert to 32bit integer
97    }
98    return hash;
99}
100
101function getSizeString(sizeInBytes) {
102    var size = sizeInBytes / 1024;
103    if(size < 1024){
104        return size.toFixed() + " KB";
105    } else {
106        size = size / 1024.00;
107        return size.toFixed(2) + " MB";
108    }
109}
110
111function getDisplayQueryHtml(urlParameters) {
112    var display = 'Document Types="';
113
114    // Build the collection ccl
115    var collections = [];
116    var collectionDisplay = [];
117    if (urlParameters["q-collection[]"] && urlParameters["q-collection[]"] instanceof Array) {
118        collections = urlParameters["q-collection[]"];
119    } else if (urlParameters["q-collection[]"]) {
120        collections.push(urlParameters["q-collection[]"].toString());
121    }
122
123    $.each(collections, function(index, value) {
124        // Build sub ccl for collection
125        if (value == "inforceActs") {
126            collectionDisplay.push("<span class='dq-highlight'>Acts</span>");
127        } else if (value == "inforceSLs") {
128            collectionDisplay.push("<span class='dq-highlight'>SL</span>");
129        } else if (value == "actsAsPassed") {
130            collectionDisplay.push("<span class='dq-highlight'>Acts as passed</span>");
131        } else if (value == "slAsMade") {
132            collectionDisplay.push("<span class='dq-highlight'>SL as made</span>");
133        } else if (value == "repealedActs") {
134            collectionDisplay.push("<span class='dq-highlight'>Repealed Acts</span>");
135        } else if (value == "repealedSLs") {
136            collectionDisplay.push("<span class='dq-highlight'>Repealed SL</span>");
137        } else if (value == "bills") {
138            collectionDisplay.push("<span class='dq-highlight'>Bills</span>");
139        } else if (value == "otherasscdocs") {
140            collectionDisplay.push("<span class='dq-highlight'>Other docs</span>");
141        } else if (value == "expnotes") {
142            collectionDisplay.push("<span class='dq-highlight'>Exp Notes</span>");
143        } else if (value == "currentLH") {
144            collectionDisplay.push("<span class='dq-highlight'>Legislative histories</span>");
145        } else if (value == "repealedLH") {
146            collectionDisplay.push("<span class='dq-highlight'>Repealed legislative histories</span>");
147        } else if (value == "website") {
148            collectionDisplay.push("<span class='dq-highlight'>Website info and help</span>");
149        }
150    });
151
152    display += collectionDisplay.join(", ") + "\"";
153
154    switch(urlParameters["q-searchform"]) {
155        case "basic":
156            if (urlParameters["q-searchfor"] == "")
157            {
158                if(urlParameters["q-year"])
159                {
160                    display += ', Year="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-year"]) + '</span>"';
161                }
162
163                if(urlParameters["q-no"])
164                {
165                    display += ', No="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-no"]) + '</span>"';
166                }
167                break;
168            }
169
170            if (urlParameters["q-searchin"] == "Content") {
171                display += ', Search In="<span class=\'dq-highlight\'>All Content</span>", ';
172            } else if (urlParameters["q-searchin"] == "Heading") {
173                display += ', Search In="<span class=\'dq-highlight\'>All Headings</span>", ';
174            } else if (urlParameters["q-searchin"] == "Title") {
175                display += ', Search In="<span class=\'dq-highlight\'>Title</span>", ';
176            } else if (urlParameters["q-searchin"] == "Part,Division") {
177                display += ', Search In="<span class=\'dq-highlight\'>Part/Division Headings</span>", ';
178            } else if (urlParameters["q-searchin"] == "Schedule") {
179                display += ', Search In="<span class=\'dq-highlight\'>Schedule Headings</span>", ';
180            } else if (urlParameters["q-searchin"] == "DefinedTerm") {
181                display += ', Search In="<span class=\'dq-highlight\'>Defined Terms</span>", ';
182            } else if (urlParameters["q-searchin"] == "Titles") {
183                display += ', Search In="<span class=\'dq-highlight\'>Historical Title</span>", ';
184            }
185
186            // Build the search term ccl
187            if (urlParameters["q-searchusing"] == "allwords") {
188                display += 'All Words="<span class=\'dq-highlight\'>';
189                display += htmlEncode(urlParameters["q-searchfor"]);
190                display += '</span>"';
191            } else if (urlParameters["q-searchusing"] == "anywords") {
192                display += 'Any Words="<span class=\'dq-highlight\'>';
193                display += htmlEncode(urlParameters["q-searchfor"]);
194                display += '</span>"';
195            } else if (urlParameters["q-searchusing"] == "exactwords") {
196                display += 'Exact Phrase="<span class=\'dq-highlight\'>';
197                display += htmlEncode(urlParameters["q-searchfor"]);
198                display += '</span>"';
199            } else if (urlParameters["q-searchusing"] == "woutwords") {
200                display += 'Without Words="<span class=\'dq-highlight\'>';
201                display += htmlEncode(urlParameters["q-searchfor"]);
202                display += '</span>"';
203            }
204
205            if(urlParameters["q-year"])
206            {
207                display += ', Year="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-year"]) + '</span>"';
208            }
209
210            if(urlParameters["q-no"])
211            {
212                display += ', No="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-no"]) + '</span>"';
213            }
214            
215            if(urlParameters["q-point-in-time"])
216            {
217                display += ', Point In Time="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-point-in-time"]) + '</span>"';
218            }
219
220            break;
221        case "advanced":
222            var getDisplayField = function(val) {
223                if (val == "Content") {
224                    return "Search in=\"<span class='dq-highlight'>All Content</span>\"";
225                } else if (val == "Heading") {
226                    return "Search in=\"<span class='dq-highlight'>All Headings</span>\"";
227                } else if (val == "Title") {
228                    return "Search in=\"<span class='dq-highlight'>Title</span>\"";
229                } else if (val == "Part,Division") {
230                    return "Search in=\"<span class='dq-highlight'>Part/Division Headings</span>\"";
231                } else if (val == "Schedule") {
232                    return "Search in=\"<span class='dq-highlight'>Schedule Headings</span>\"";
233                } else if (val == "DefinedTerm") {
234                    return "Search in=\"<span class='dq-highlight'>Defined Terms</span>\"";
235                }
236                else
237                {
238                    return "Unknown Field";
239                }
240            }
241            var rowDisplays = [];
242            var joinOperators = [];
243
244            $.each(urlParameters["q-searchfor[]"], function(idx, val){
245                // Skip empty ones
246                if(val === "")
247                    return;
248
249                var rowDisplay = " ";
250
251                if (urlParameters["q-searchusing[]"][idx] == "allwords") {
252                    var allWordsExp = val.split(/\s+/).cclTermSafe().join(" AND ");
253
254                    rowDisplay += getDisplayField(urlParameters["q-searchin[]"][idx]);
255                    rowDisplay += ", Where";
256                    rowDisplay += " All Words=\"<span class='dq-highlight'>" + htmlEncode(urlParameters["q-searchfor[]"][idx]);
257                    rowDisplay += "</span>\"";
258
259                } else if (urlParameters["q-searchusing[]"][idx] == "anywords") {
260                    var anyWordsExp = val.split(/\s+/).cclTermSafe().join(" OR ");
261
262                    rowDisplay += getDisplayField(urlParameters["q-searchin[]"][idx]);
263                    rowDisplay += ", Where";
264                    rowDisplay += " Any Words=\"<span class='dq-highlight'>" + htmlEncode(urlParameters["q-searchfor[]"][idx]);
265                    rowDisplay += "</span>\"";
266
267                } else if (urlParameters["q-searchusing[]"][idx] == "exactwords") {
268
269                    rowDisplay += getDisplayField(urlParameters["q-searchin[]"][idx]);
270                    rowDisplay += ", Where";
271                    rowDisplay += " Exact Phrase=\"<span class='dq
271-highlight'>" + htmlEncode(urlParameters["q-searchfor[]"][idx]);
272                    rowDisplay += "</span>\"";
273
274                } else if (urlParameters["q-searchusing[]"][idx] == "woutwords") {
275
276                    rowDisplay += getDisplayField(urlParameters["q-searchin[]"][idx]);
277                    rowDisplay += ", Where";
278                    rowDisplay += " Without Words=\"<span class='dq-highlight'>" + htmlEncode(urlParameters["q-searchfor[]"][idx]);
279                    rowDisplay += "</span>\"";
280                }
281
282                // Collect the operator if not on the first
283                if(idx !== 0)
284                {
285                    joinOperators.push(urlParameters["q-operator[]"][idx]);
286                }
287
288                rowDisplays.push(rowDisplay);
289            });
290
291
292
293            // Now join each query row with the operator
294            var rowsDisplayQuery = "";
295            $.each(rowDisplays, function(idx, val){
296                rowsDisplayQuery += val;
297
298                if(joinOperators[idx])
299                    rowsDisplayQuery += " <b>" + joinOperators[idx] + "</b> ";
300            });
301
302            display += rowsDisplayQuery;
303
304            if(urlParameters["q-years"])
305            {
306                display += ', Year="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-years"]) + '</span>"';
307            }
308
309            if(urlParameters["q-no"])
310            {
311                display += ', No="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-no"]) + '</span>"';
312            }
313            
314            if(urlParameters["q-point-in-time"])
315            {
316                display += ', Point In Time="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-point-in-time"]) + '</span>"';
317            }
318            break;
319        case "menuAssisted":
320            display += ', ';
321
322            display += "Query=\"<span class='dq-highlight'>"
323                    + htmlEncode(urlParameters["q-queryBuilderOutput"].replace(/([ ](AND|OR|NOT|WITH|SAME|!10|%10))$/, ''));
324            display += "</span>\"";
325
326            if(urlParameters["q-years"])
327            {
328                display += ', Year="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-years"]) + '</span>"';
329            }
330
331            if(urlParameters["q-no"])
332            {
333                display += ', No="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-no"]) + '</span>"';
334            }
335
336            if(urlParameters["q-point-in-time"])
337            {
338                display += ', Point In Time="<span class=\'dq-highlight\'>' + htmlEncode(urlParameters["q-point-in-time"]) + '</span>"';
339            }
340            break;
341    }
342    return display;
343}
344
345// Ideally we would overwrite the fnStateSave and fnStateLoad functions of datatable, but
346// this would require us to replicate various settings when the cookie is missing, so
347// it is simpler to use the function below when rendering a table instead, disabling the
348// state saving functionality of datatables altogether.
349
350function renderTable(table) {
351	table.pageLength = $.jStorage.get("results-per-page");
352	if (table.pageLength == null){
353	    table.pageLength = 10;
354	}
355
356	// Show the page that the user was on when previously viewing this table.
357	if (window.sessionStorage.getItem("table-history")) {
358	    var pageDetails = JSON.parse(window.sessionStorage.getItem("table-history"))[window.location.pathname + $.address.value()];
359	    if (pageDetails) {
360	        table.displayStart = pageDetails.displayStart;
361
362	        if (pageDetails.sortFields && pageDetails.sortDirections) {
363                // Ensure that the sort order that the user previously used for this table is remembered for this session.
364                var foundIndexes = [];
365                for (var j = 0; j < pageDetails.sortFields.length; j++) {
366                    for (var i = 0; i < table.columns.length; i++) {
367                        if (table.columns[i].key === pageDetails.sortFields[j] ||
368                            table.columns[i].sortKey === pageDetails.sortFields[j]) {
369
370                            foundIndexes.push(i);
371                        } else if ((j === 0) && (table.columns[i].sortable === "defaultAsc" || table.columns[i].sortable === "defaultDesc")) {
372                            table.columns[i].sortable = true;
373                        }
374                    }
375                }
376                for (var k = 0; k < foundIndexes.length; k++) {
377                    var append = true;
378                    if (foundIndexes.length > k + 1) {
379                        append = foundIndexes[k + 1] > foundIndexes[k];
380                    }
381                    if (pageDetails.sortDirections[k] === "asc") {
382                        if (append) {
383                            table.columns[foundIndexes[k]].sortable = "defaultAsc";
384                        } else {
385                            table.columns[foundIndexes[k]].sortable = "defaultAscPrepend";
386                        }
387                    } else {
388                        if (append) {
389                            table.columns[foundIndexes[k]].sortable = "defaultDesc";
390                        } else {
391                            table.columns[foundIndexes[k]].sortable = "defaultDescPrepend";
392                        }
393                    }
394                }
395            }
396        } else {
397	        table.displayStart = 0;
398        }
399    }
400    table.loadSettings({
401        "pageSize": table.pageLength,
402        "displayStart": table.displayStart
403    });
404	table.render();
405
406	// Update the table history session variable to ensure that we remember what page the user was viewing when
407    // they last looked at the same table (search and browse results).
408	table.oTable.on('draw.dt', function() {
409	    if ($.address.value() !== "/") {
410            var jsonObj;
411            if (window.sessionStorage.getItem("table-history")) {
412                jsonObj = JSON.parse(window.sessionStorage.getItem("table-history"));
413            } else {
414                jsonObj = {};
415            }
416            jsonObj[window.location.pathname + $.address.value()] = {
417                displayStart: table.oTable.fnPagingInfo().iStart,
418                sortFields: table.sortFields,
419                sortDirections: table.sortDirections
420            };
421            window.sessionStorage.setItem("table-history", JSON.stringify(jsonObj));
422        }
423    });
424	$("select[name=table_1_length]").change(function() {
425        $.jStorage.set("results-per-page", table.oTable.fnPagingInfo().iLength);
426	});
427}
428
429//Other rendition functions for DALTables
430function purchaseLegislation(recordObj) {
431    var j = new Array();                
432	var list = new Array();
433	var count = 0;
434	var expression = 'VersionSeriesId="' + recordObj['version.series.id'].__value__ + '"';
435	$.ajax({
436	    type : "GET",
437	    url : "/projectdata",
438	    dataType : "json",
439	    data : {
440	        ds : "OQPC-LegHistoryAssociatedDataSource",
441	        expression : expression
442	    }
443	}).done(function(data) 
444	{
445	    if(data.data) 
446	    {
447	        count = data.data.length;
448            for (var i = 0; i < count; i++) 
449            {
450                j.push(data.data[i].__value__.__children__);
451                j.push(window.location.origin + createUrl(data.data[i].__value__, null, false, null, "pdf"));
452                list.push(j);
453                j = [];
454            }
455        }
456        var origUrl = window.location.origin + createUrl(recordObj, null, false, null, "pdf");
457        var origName = recordObj['title'].__value__;
458        var titleExists = 0;
459        //Check each entry of the list to see if it's the original URL of the title
460        for (var i = 0; i < count; i++) 
461        {
462            //If it does match, replace it with the original name rather than the //associateddoc name and move it to the front of the array
463            if(list[i][1] == origUrl) 
464            {    
465                list[i][0] = origName;
466                j = list[i];
467                list.splice(i, 1);
468                list.unshift(j);
469                titleExists = 1;                            
470            }
471        }
472        //If it doesn't match, add the original title and URL to the front of the array
473        if(titleExists === 0)
474        {
475            j.push(origName);
476            j.push(origUrl);
477            list.unshift(j);
478        }                                               
479        if(window.lit)
480        {
481            window.lit.purchasePrintedCopy(list); 
482        }   
483        return;
484    });  
485}
486
487function downloadOtherRendition(type, recordObj) {
488    var regExp = new RegExp("^.*" + type + "$");
489    if(recordObj.otherRenditions) {
490        if (!$.isArray(recordObj.otherRenditions)) {
491           recordObj.otherRenditions = [recordObj.otherRenditions];
492        }
493        for (var i = 0; i < recordObj.otherRenditions.length; i++) {
494            if(recordObj.otherRenditions[i]['mimetype'].match(regExp) !== null) {
495                window.open((createUrl(recordObj, null, false, null, type.toLowerCase())), '_blank');
496                return;
497            }
498        }
499    }
500    var html = '<div class="alert alert-danger alert-dismissible" role="alert">'
501             + '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>'
502             + ' No ' + type + ' available for the selected title.'
503             + '</div>';
504    $('#facet-btn-group').before(html);
505    $('#browse-results').before(html);
506}
507
508function downloadXML(recordObj) {    
509    var regExp = new RegExp("^(act|sl)$");
510    if(recordObj.type) {
511        if(recordObj.type['__value__'].match(regExp) !== null) {
512            window.open((createUrl(recordObj, null, false, null, "xml")), '_blank');
513            return;
514        }
515    }
516    var html = '<div class="alert alert-danger alert-dismissible" role="alert">'
517             + '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>'
518             + ' No XML available for the selected title.'
519             + '</div>';
520    $('#facet-btn-group').before(html);
521    $('#browse-results').before(html);
522}
523
524/**
525 * window.location.origin does not exist in IE8 
526 */
527if(!window.location.origin) 
528{
529    window.location.origin = window.location.protocol + "//" + 
530                             window.location.hostname + 
531                             (window.location.port ? ':' + window.location.port: '');
532}
533
534function htmlEncode(value){
535  //create a in-memory div, set it's inner text(which jQuery automatically encodes)
536  //then grab the encoded contents back out.  The div never exists on the page.
537  return $('<div/>').text(value).html();
538}
539
540function htmlDecode(value){
541  return $('<div/>').html(value).text();
542}
543
544function logout() {
545	$.ajax({
546	    type: "POST",
547	    url: '/admin/doAjaxLogout',
548	    success: function()
549	    {
550	    	$.removeCookie('logged-on', { path: '/' })
551	        window.location.reload();
552	    }
553	}); 
554}
555
556/*** END UTILS ********************************************/
557
558/*** EVENT LISTENERS **************************************/
559$(document).on('keydown', function(e) {
560    if (e.key === 'Escape') {
561        const $tocContainer = $('#toc-col');
562        if ($tocContainer[0].contains(document.activeElement)) {
563            e.preventDefault();
564            $('#legBtnGroup a').eq(0).trigger("focus");
565        }
566    }
567});
568/*** END EVENT LISTENERS **************************************/
569
570/**
571 * This file contains a random language scroller. It updates
572 * the text of the other languages link every 5 seconds with a
573 * new random language supported by the www.qld.gov.au/languages
574 * 
575 * (based on ideas from http://www.communityservices.qld.gov.au/scripts/languages.js)
576 * 
577 * @requires jquery
578 */
579if (typeof jQuery != 'undefined') { /* Start if jQuery exists */
580(function($) { /* start closure */
581    
582    var languages = [
583        '<span lang=\"ar\">العربية</span>',
584        '<span lang=\"el\">Ελληνικά</span>',
585        '<span lang=\"pl\">Polski</span>',
586        '<span lang=\"bs\">Bosanksi</span>',
587        '<span lang=\"id\">Bahasa Indonesia</span>',
588        '<span lang=\"ru\">Русский</span>',
589        '<span lang=\"zh\">中文</span>',
590        '<span lang=\"it\">Italiano</span>',        
591        '<span lang=\"sr\">српски</span>'   ,
592        '<span lang=\"hr\">Hrvatski</span>',
593        '<span lang=\"ja\">日本語</span>',
594        '<span lang=\"es\">Español</span>',
595        '<span lang=\"fr\">Français</span>',
596        '<span lang=\"ko\">한국어</span>',
597        '<span lang=\"tl\">Tagalog</span>',
598        '<span lang=\"de\">
598Deutsch</span>',
599        '<span lang=\"fa\">فارسی</span>',
600        '<span lang=\"vi\">Tiếng Việt</span>'
601    ];
602    
603    var doctype = document.doctype ? document.doctype.publicId : document.firstChild.data;
604    var XHTML;
605    if(doctype)
606    {
607        XHTML = doctype.indexOf('XHTML') > 0;
608    }
609
610    var updateLanguage = function () {
611        $('#languages a').empty().append('Other languages (' + languages[Math.floor(Math.random()*languages.length)]+')');
612        //$('#languages a').empty().append(languages[Math.floor(Math.random()*languages.length)]+' (Other languages)');
613        // copy @lang to @xml:lang for XHTML
614        if (XHTML) {
615            $('#languages span').attr('xml:lang', $('#languages span').attr('lang'));
616        }
617    };
618    
619    updateLanguage();
620    window.setInterval(updateLanguage, 5000);
621    
622
623})(jQuery); /* end closure */
624} /* end if jQuery exists */
625
626/** END LANGUAGE ********************************/
627
628/**
629 *  Website resize functions and variables 
630 */
631
632var g_minF = 100;
633var g_maxF = 200;
634
635$(function() {
636	$("#resizeUp").click(function() 
637	{
638		$("#resizeDown").prop("disabled", false);
639	    //var fs = $("html").css("font-size");
640	    //fs = parseInt(fs.replace("px",""));
641	    var fs = $.jStorage.get("currFontSize") + 10;
642	    $.jStorage.set("currFontSize", fs);
643	    if(fs >= g_maxF) {
644	    	$("#resizeUp").blur();
645	    	$("#resizeUp").prop("disabled", true);
646	    }
647	    fs = fs + "%";
648	    $("#main").css("font-size", fs);
649	});
650	
651	function resizeZero() {
652	    $.jStorage.set("currFontSize", g_minF);
653	    $("html").css("font-size", g_minF + "%");
654	}
655	
656	$("#resizeDown").click(function() 
657	{	
658		$("#resizeUp").prop("disabled", false);
659	    //var fs = $("html").css("font-size");
660	    //fs = parseInt(fs.replace("px",""));
661	    var fs = $.jStorage.get("currFontSize") - 10;
662        $.jStorage.set("currFontSize", fs);
663        if(fs <= g_minF) {
664	    	$("#resizeDown").blur();
665	    	$("#resizeDown").prop("disabled", true);
666	    }
667	    fs = fs + "%";
668        $("#main").css("font-size", fs);
669	        
670	});
671});
672
673/** END RESIZE ************************************************/
674
675/**
676 * Function for the 'Skip to main content' button
677 */
678
679$(function(){
680    $("#skipStart").click(function(){
681        $('html, body').animate({
682            scrollTop: $("#main").offset().top
683        }, 200);
684        $("#skipEnd").focus();
685    });
686});
687
688/** END SKIP **************************************************/
689
690/*! sprintf.js | Copyright (c) 2007-2013 Alexandru Marasteanu <hello at alexei dot ro> | 3 clause BSD license */
691
692(function(ctx) {
693    var sprintf = function() {
694        if (!sprintf.cache.hasOwnProperty(arguments[0])) {
695            sprintf.cache[arguments[0]] = sprintf.parse(arguments[0]);
696        }
697        return sprintf.format.call(null, sprintf.cache[arguments[0]], arguments);
698    };
699
700    sprintf.format = function(parse_tree, argv) {
701        var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
702        for (i = 0; i < tree_length; i++) {
703            node_type = get_type(parse_tree[i]);
704            if (node_type === 'string') {
705                output.push(parse_tree[i]);
706            }
707            else if (node_type === 'array') {
708                match = parse_tree[i]; // convenience purposes only
709                if (match[2]) { // keyword argument
710                    arg = argv[cursor];
711                    for (k = 0; k < match[2].length; k++) {
712                        if (!arg.hasOwnProperty(match[2][k])) {
713                            throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
714                        }
715                        arg = arg[match[2][k]];
716                    }
717                }
718                else if (match[1]) { // positional argument (explicit)
719                    arg = argv[match[1]];
720                }
721                else { // positional argument (implicit)
722                    arg = argv[cursor++];
723                }
724
725                if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
726                    throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
727                }
728                switch (match[8]) {
729                    case 'b': arg = arg.toString(2); break;
730                    case 'c': arg = String.fromCharCode(arg); break;
731                    case 'd': arg = parseInt(arg, 10); break;
732                    case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
733                    case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
734                    case 'o': arg = arg.toString(8); break;
735                    case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
736                    case 'u': arg = arg >>> 0; break;
737                    case 'x': arg = arg.toString(16); break;
738                    case 'X': arg = arg.toString(16).toUpperCase(); break;
739                }
740                arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
741                pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
742                pad_length = match[6] - String(arg).length;
743                pad = match[6] ? str_repeat(pad_character, pad_length) : '';
744                output.push(match[5] ? arg + pad : pad + arg);
745            }
746        }
747        return output.join('');
748    };
749
750    sprintf.cache = {};
751
752    sprintf.parse = function(fmt) {
753        var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
754        while (_fmt) {
755            if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
756                parse_tree.push(match[0]);
757            }
758            else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
759                parse_tree.push('%');
760            }
761            else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
762                if (match[2]) {
763                    arg_names |= 1;
764                    var field_list = [], replacement_field = match[2], field_match = [];
765                    if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
766                        field_list.push(field_match[1]);
767                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
768                            if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
769                                field_list.push(field_match[1]);
770                            }
771                            else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
772                                field_list.push(field_match[1]);
773                            }
774                            else {
775                                throw('[sprintf] huh?');
776                            }
777                        }
778                    }
779                    else {
780                        throw('[sprintf] huh?');
781                    }
782                    match[2] = field_list;
783                }
784                else {
785                    arg_names |= 2;
786                }
787                if (arg_names === 3) {
788                    throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
789                }
790                parse_tree.push(match);
791            }
792            else {
793                throw('[sprintf] huh?');
794            }
795            _fmt = _fmt.substring(match[0].length);
796        }
797        return parse_tree;
798    };
799
800    var vsprintf = function(fmt, argv, _argv) {
801        _argv = argv.slice(0);
802        _argv.splice(0, 0, fmt);
803        return sprintf.apply(null, _argv);
804    };
805
806    /**
807     * helpers
808     */
809    function get_type(variable) {
810        return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
811    }
812
813    function str_repeat(input, multiplier) {
814        for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
815        return output.join('');
816    }
817
818    /**
819     * export to either browser or node.js
820     */
821    ctx.sprintf = sprintf;
822    ctx.vsprintf = vsprintf;
823})(typeof exports != "undefined" ? exports : window);
824
825/**** sprintf ********************************************************************************************/
826
827
828/**** jquery cookie library ***/
829/*!
830 * jQuery Cookie Plugin v1.4.1
831 * https://github.com/carhartl/jquery-cookie
832 *
833 * Copyright 2013 Klaus Hartl
834 * Released under the MIT license
835 */
836(function (factory) {
837    if (typeof define === 'function' && define.amd) {
838        // AMD
839        define(['jquery'], factory);
840    } else if (typeof exports === 'object') {
841        // CommonJS
842        factory(require('jquery'));
843    } else {
844        // Browser globals
845        factory(jQuery);
846    }
847}(function ($) {
848
849    var pluses = /\+/g;
850
851    function encode(s) {
852        return config.raw ? s : encodeURIComponent(s);
853    }
854
855    function decode(s) {
856        return config.raw ? s : decodeURIComponent(s);
857    }
858
859    function stringifyCookieValue(value) {
860        return encode(config.json ? JSON.stringify(value) : String(value));
861    }
862
863    function parseCookieValue(s) {
864        if (s.indexOf('"') === 0) {
865            // This is a quoted cookie as according to RFC2068, unescape...
866            s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
867        }
868
869        try {
870            // Replace server-side written pluses with spaces.
871            // If we can't decode the cookie, ignore it, it's unusable.
872            // If we can't parse the cookie, ignore it, it's unusable.
873            s = decodeURIComponent(s.replace(pluses, ' '));
874            return config.json ? JSON.parse(s) : s;
875        } catch(e) {}
876    }
877
878    function read(s, converter) {
879        var value = config.raw ? s : parseCookieValue(s);
880        return $.isFunction(converter) ? converter(value) : value;
881    }
882
883    var config = $.cookie = function (key, value, options) {
884
885        // Write
886
887        if (value !== undefined && !$.isFunction(value)) {
888            options = $.extend({}, config.defaults, options);
889
890            if (typeof options.expires === 'number') {
891                var days = options.expires, t = options.expires = new Date();
892                t.setTime(+t + days * 864e+5);
893            }
894
895            return (document.cookie = [
896                encode(key), '=', stringifyCookieValue(value),
897                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
898                options.path    ? '; path=' + options.path : '',
899                options.domain  ? '; domain=' + options.domain : '',
900                options.secure  ? '; secure' : ''
901            ].join(''));
902        }
903
904        // Read
905
906        var result = key ? undefined : {};
907
908        // To prevent the for loop in the first place assign an empty array
909        // in case there are no cookies at all. Also prevents odd result when
910        // calling $.cookie().
911        var cookies = document.cookie ? document.cookie.split('; ') : [];
912
913        for (var i = 0, l = cookies.length; i < l; i++) {
914            var parts = cookies[i].split('=');
915            var name = decode(parts.shift());
916            var cookie = parts.join('=');
917
918            if (key && key === name) {
919                // If second argument (value) is a function it's a converter...
920                result = read(cookie, value);
921                break;
922            }
923
924            // Prevent storing a cookie that we couldn't decode.
925            if (!key && (cookie = read(cookie)) !== undefined) {
926                result[name] = cookie;
927            }
928        }
929
930        return result;
931    };
932
933    config.defaults = {};
934
935    $.removeCookie = function (key, options) {
936        if ($.cookie(key) === undefined) {
937            return false;
938        }
939
940        // Must not alter options, thus extending a fresh object...
941        $.cookie(key, '', $.extend({}, options, { expires: -1 }));
942        return !$.cookie(key);
943    };
944
945}));
946
947/*************************************************************************************/
948
949/*!
950 * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
951 * http://benalman.com/projects/jquery-bbq-plugin/
952 * 
953 * Copyright (c) 2010 "Cowboy" Ben Alman
954 * Dual licensed under the MIT and GPL licenses.
955 * http://benalman.com/about/license/
956 */
957
958// Script: jQuery BBQ: Back Button & Query Library
959//
960// *Version: 1.2.1, Last updated: 2/17/2010*
961// 
962// Project Home - http://benalman.com/projects/jquery-bbq-plugin/
963// GitHub       - http://github.com/cowboy/jquery-bbq/
964// Source       - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
965// (Minified)   - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (4.0kb)
966// 
967// About: License
968// 
969// Copyright (c) 2010 "Cowboy" Ben Alman,
970// Dual licensed under the MIT and GPL licenses.
971// http://benalman.com/about/license/
972// 
973// About: Examples
974// 
975// These working examples, complete with fully commented code, illustrate a few
976// ways in which this plugin can be used.
977// 
978// Basic AJAX     - http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
979// Advanced AJAX  - http://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
980// jQuery UI Tabs - http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
981// Deparam        - http://benalman.com/code/projects/jquery-bbq/examples/deparam/
982// 
983// About: Support and Testing
984// 
985// Information about what version or versions of jQuery this plugin has been
986// tested with, what browsers it has been tested in, and where the unit tests
987// reside (so you can test it yourself).
988// 
989// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
990// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4,
991//                   Chrome 4-5, Opera 9.6-10.1.
992// Unit Tests      - http://benalman.com/code/projects/jquery-bbq/unit/
993// 
994// About: Release History
995// 
996// 1.2.1 - (2/17/2010) Actually fixed the stale window.location Safari bug from
997//         <jQuery hashchange event> in BBQ, which was the main reason for the
998//         previous release!
999// 1.2   - (2/16/2010) Integrated <jQuery hashchange event> v1.2, which fixes a
1000//         Safari bug, the event can now be bound before DOM ready, and IE6/7
1001//         page should no longer scroll when the event is first bound. Also
1002//         added the <jQuery.param.fragment.noEscape> method, and reworked the
1003//         <hashchange event (BBQ)> internal "add" method to be compatible with
1004//         changes made to the jQuery 1.4.2 special events API.
1005// 1.1.1 - (1/22/2010) Integrated <jQuery hashchange event> v1.1, which fixes an
1006//         obscure IE8 EmulateIE7 meta tag compatibility mode bug.
1007// 1.1   - (1/9/2010) Broke out the jQuery BBQ event.special <hashchange event>
1008//         functionality into a separate plugin for users who want just the
1009//         basic event & back button support, without all the extra awesomeness
1010//         that BBQ provides. This plugin will be included as part of jQuery BBQ,
1011//         but also be available separately. See <jQuery hashchange event>
1012//         plugin for more information. Also added the <jQuery.bbq.removeState>
1013//         method and added additional <jQuery.deparam> examples.
1014// 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
1015//         location.hash would report incorrectly if the hash contained the ?
1016//         character. Also <jQuery.param.querystring> and <jQuery.param.fragment>
1017//         will no longer parse params out of a URL that doesn't contain ? or #,
1018//         respectively.
1019// 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
1020//         a "This page contains both secure and nonsecure items." warning when
1021//         used on an https:// page.
1022// 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
1023//         Compatibility View" modes erroneously report that the browser
1024//         supports the native window.onhashchange event, a slightly more
1025//         robust test needed to be added.
1026// 1.0   - (10/2/2009) Initial release
1027
1028jQuery.browser = {};
1029(function () {
1030    jQuery.browser.msie = false;
1031    jQuery.browser.version = 0;
1032    if (navigator.userAgent.match(/MSIE ([0-9]+)\./)) {
1033        jQuery.browser.msie = true;
1034        jQuery.browser.version = RegExp.$1;
1035    }
1036})();
1037
1038(function($,window){
1039  '$:nomunge'; // Used by YUI compressor.
1040  
1041  // Some convenient shortcuts.
1042  var undefined,
1043    aps = Array.prototype.slice,
1044    decode = decodeURIComponent,
1045    
1046    // Method / object references.
1047    jq_param = $.param,
1048    jq_param_fragment,
1049    jq_deparam,
1050    jq_deparam_fragment,
1051    jq_bbq = $.bbq = $.bbq || {},
1052    jq_bbq_pushState,
1053    jq_bbq_getState,
1054    jq_elemUrlAttr,
1055    jq_event_special = $.event.special,
1056    
1057    // Reused strings.
1058    str_hashchange = 'hashchange',
1059    str_querystring = 'querystring',
1060    str_fragment = 'fragment',
1061    str_elemUrlAttr = 'elemUrlAttr',
1062    str_location = 'location',
1063    str_href = 'href',
1064    str_src = 'src',
1065    
1066    // Reused RegExp.
1067    re_trim_querystring = /^.*\?|#.*$/g,
1068    re_trim_fragment = /^.*\#/,
1069    re_no_escape,
1070    
1071    // Used by jQuery.elemUrlAttr.
1072    elemUrlAttr_cache = {};
1073  
1074  // A few commonly used bits, broken out to help reduce minified file size.
1075  
1076  function is_string( arg ) {
1077    return typeof arg === 'string';
1078  };
1079  
1080  // Why write the same function twice? Let's curry! Mmmm, curry..
1081  
1082  function curry( func ) {
1083    var args = aps.call( arguments, 1 );
1084    
1085    return function() {
1086      return func.apply( this, args.concat( aps.call( arguments ) ) );
1087    };
1088  };
1089  
1090  // Get location.hash (or what you'd expect location.hash to be) sans any
1091  // leading #. Thanks for making this necessary, Firefox!
1092  function get_fragment( url ) {
1093    return url.replace( /^[^#]*#?(.*)$/, '$1' );
1094  };
1095  
1096  // Get location.search (or what you'd expect location.search to be) sans any
1097  // leading #. Thanks for making this necessary, IE6!
1098  function get_querystring( url ) {
1099    return url.replace( /(?:^[^?#]*\?([^#]*).*$)?.*/, '$1' );
1100  };
1101  
1102  // Section: Param (to string)
1103  // 
1104  // Method: jQuery.param.querystring
1105  // 
1106  // Retrieve the query string from a URL or if no arguments are passed, the
1107  // current window.location.
1108  // 
1109  // Usage:
1110  // 
1111  // > jQuery.param.querystring( [ url ] );
1112  // 
1113  // Arguments:
1114  // 
1115  //  url - (String) A URL containing query string params to be parsed. If url
1116  //    is not passed, the current window.location is used.
1117  // 
1118  // Returns:
1119  // 
1120  //  (String) The parsed query string, with any leading "?" removed.
1121  //
1122  
1123  // Method: jQuery.param.querystring (build url)
1124  // 
1125  // Merge a URL, with or without pre-existing query string params, plus any
1126  // object, params string or URL containing query string params into a new URL.
1127  // 
1128  // Usage:
1129  // 
1130  // > jQuery.param.querystring( url, params [, merge_mode ] );
1131  // 
1132  // Arguments:
1133  // 
1134  //  url - (String) A valid URL for params to be merged into. This URL may
1135  //    contain a query string and/or fragment (hash).
1136  //  params - (String) A params string or URL containing query string params to
1137  //    be merged into url.
1138  //  params - (Object) A params object to be merged into url.
1139  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
1140  //    specified, and is as-follows:
1141  // 
1142  //    * 0: params in the params argument will override any query string
1143  //         params in url.
1144  //    * 1: any query string params in url will override params in the params
1145  //         argument.
1146  //    * 2: params argument will completely replace any query string in url.
1147  // 
1148  // Returns:
1149  // 
1150  //  (String) Either a params string with urlencoded data or a URL with a
1151  //    urlencoded query string in the format 'a=b&c=d&e=f'.
1152  
1153  // Method: jQuery.param.fragment
1154  // 
1155  // Retrieve the fragment (hash) from a URL or if no arguments are passed, the
1156  // current window.location.
1157  // 
1158  // Usage:
1159  // 
1160  // > jQuery.param.fragment( [ url ] );
1161  // 
1162  // Arguments:
1163  // 
1164  //  url - (String) A URL containing fragment (hash) params to be parsed. If
1165  //    url is not passed, the current window.location is used.
1166  // 
1167  // Returns:
1168  // 
1169  //  (String) The parsed fragment (hash) string, with any leading "#" removed.
1170  
1171  // Method: jQuery.param.fragment (build url)
1172  // 
1173  // Merge a URL, with or without pre-existing fragment (hash) params, plus any
1174  // object, params string or URL containing fragment (hash) params into a new
1175  // URL.
1176  // 
1177  // Usage:
1178  // 
1179  // > jQuery.param.fragment( url, params [, merge_mode ] );
1180  // 
1181  // Arguments:
1182  // 
1183  //  url - (String) A valid URL for params to be merged into. This URL may
1184  //    contain a query string and/or fragment (hash).
1185  //  params - (String) A params string or URL containing fragment (hash) params
1186  //    to be merged into url.
1187  //  params - (Object) A params object to be merged into url.
1188  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
1189  //    specified, and is as-follows:
1190  // 
1191  //    * 0: params in the params argument will override any fragment (hash)
1192  //         params in url.
1193  //    * 1: any fragment (hash) params in url will override params in the
1194  //         params argument.
1195  //    * 2: params argument will completely replace any query string in url.
1196  // 
1197  // Returns:
1198  // 
1199  //  (String) Either a params string with urlencoded data or a URL with a
1200  //    urlencoded fragment (hash) in the format 'a=b&c=d&e=f'.
1201  
1202  function jq_param_sub( is_fragment, get_func, url, params, merge_mode ) {
1203    var result,
1204      qs,
1205      matches,
1206      url_params,
1207      hash;
1208    
1209    if ( params !== undefined ) {
1210      // Build URL by merging params into url string.
1211      
1212      // matches[1] = url part that precedes params, not including trailing ?/#
1213      // matches[2] = params, not including leading ?/#
1214      // matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
1215      matches = url.match( is_fragment ? /^([^#]*)\#?(.*)$/ : /^([^#?]*)\??([^#]*)(#?.*)/ );
1216      
1217      // Get the hash if in 'querystring' mode, and it exists.
1218      hash = matches[3] || '';
1219      
1220      if ( merge_mode === 2 && is_string( params ) ) {
1221        // If merge_mode is 2 and params is a string, merge the fragment / query
1222        // string into the URL wholesale, without converting it into an object.
1223        qs = params.replace( is_fragment ? re_trim_fragment : re_trim_querystring, '' );
1224        
1225      } else {
1226        // Convert relevant params in url to object.
1227        url_params = jq_deparam( matches[2] );
1228        
1229        params = is_string( params )
1230          
1231          // Convert passed params string into object.
1232          ? jq_deparam[ is_fragment ? str_fragment : str_querystring ]( params )
1233          
1234          // Passed params object.
1235          : params;
1236        
1237        qs = merge_mode === 2 ? params                              // passed params replace url params
1238          : merge_mode === 1  ? $.extend( {}, params, url_params )  // url params override passed params
1239          : $.extend( {}, url_params, params );                     // passed params override url params
1240        
1241        // Convert params object to a string.
1242        qs = jq_param( qs );
1243        
1244        // Unescape characters specified via $.param.noEscape. Since only hash-
1245        // history users have requested this feature, it's only enabled for
1246        // fragment-related params strings.
1247        if ( is_fragment ) {
1248          qs = qs.replace( re_no_escape, decode );
1249        }
1250      }
1251      
1252      // Build URL from the base url, querystring and hash. In 'querystring'
1253      // mode, ? is only added if a query string exists. In 'fragment' mode, #
1254      // is always added.
1255      result = matches[1] + ( is_fragment ? '#' : qs || !matches[1] ? '?' : '' ) + qs + hash;
1256      
1257    } else {
1258      // If URL was passed in, parse params from URL string, otherwise parse
1259      // params from window.location.
1260      result = get_func( url !== undefined ? url : window[ str_location ][ str_href ] );
1261    }
1262    
1263    return result;
1264  };
1265  
1266  jq_param[ str_querystring ]                  = curry( jq_param_sub, 0, get_querystring );
1267  jq_param[ str_fragment ] = jq_param_fragment = curry( jq_param_sub, 1, get_fragment );
1268  
1269  // Method: jQuery.param.fragment.noEscape
1270  // 
1271  // Specify characters that will be left unescaped when fragments are created
1272  // or merged using <jQuery.param.fragment>, or when the fragment is modified
1273  // using <jQuery.bbq.pushState>. This option only applies to serialized data
1274  // object fragments, and not set-as-string fragments. Does not affect the
1275  // query string. Defaults to ",/" (comma, forward slash).
1276  // 
1277  // Note that this is considered a purely aesthetic option, and will help to
1278  // create URLs that "look pretty" in the address bar or bookmarks, without
1279  // affecting functionality in any way. That being said, be careful to not
1280  // unescape characters that are used as delimiters or serve a special
1281  // purpose, such as the "#?&=+" (octothorpe, question mark, ampersand,
1282  // equals, plus) characters.
1283  // 
1284  // Usage:
1285  // 
1286  // > jQuery.param.fragment.noEscape( [ chars ] );
1287  // 
1288  // Arguments:
1289  // 
1290  //  chars - (String) The characters to not escape in the fragment. If
1291  //    unspecified, defaults to empty string (escape all characters).
1292  // 
1293  // Returns:
1294  // 
1295  //  Nothing.
1296  
1297  jq_param_fragment.noEscape = function( chars ) {
1298    chars = chars || '';
1299    var arr = $.map( chars.split(''), encodeURIComponent );
1300    re_no_escape = new RegExp( arr.join('|'), 'g' );
1301  };
1302  
1303  // A sensible default. These are the characters people seem to complain about
1304  // "uglifying up the URL" the most.
1305  jq_param_fragment.noEscape( ',/' );
1306  
1307  // Section: Deparam (from string)
1308  // 
1309  // Method: jQuery.deparam
1310  // 
1311  // Deserialize a params string into an object, optionally coercing numbers,
1312  // booleans, null and undefined values; this method is the counterpart to the
1313  // internal jQuery.param method.
1314  // 
1315  // Usage:
1316  // 
1317  // > jQuery.deparam( params [, coerce ] );
1318  // 
1319  // Arguments:
1320  // 
1321  //  params - (String) A params string to be parsed.
1322  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
1323  //    undefined to their actual value. Defaults to false if omitted.
1324  // 
1325  // Returns:
1326  // 
1327  //  (Object) An object representing the deserialized params string.
1328  
1329  $.deparam = jq_deparam = function( params, coerce ) {
1330    var obj = {},
1331      coerce_types = { 'true': !0, 'false': !1, 'null': null };
1332    
1333    // Iterate over all name=value pairs.
1334    $.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){
1335      var param = v.split( '=' ),
1336        key = decode( param[0] ),
1337        val,
1338        cur = obj,
1339        i = 0,
1340        
1341        // If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
1342        // into its component parts.
1343        keys = key.split( '][' ),
1344        keys_last = keys.length - 1;
1345      
1346      // If the first keys part contains [ and the last ends with ], then []
1347      // are correctly balanced.
1348      if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
1349        // Remove the trailing ] from the last keys part.
1350        keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
1351        
1352        // Split first keys part into two parts on the [ and add them back onto
1353        // the beginning of the keys array.
1354        keys = keys.shift().split('[').concat( keys );
1355        
1356        keys_last = keys.length - 1;
1357      } else {
1358        // Basic 'foo' style key.
1359        keys_last = 0;
1360      }
1361      
1362      // Are we dealing with a name=value pair, or just a name?
1363      if ( param.length === 2 ) {
1364        val = decode( param[1] );
1365        
1366        // Coerce values.
1367        if ( coerce ) {
1368          val = val && !isNaN(val)            ? +val              // number
1369            : val === 'undefined'             ? undefined         // undefined
1370            : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
1371            : val;                                                // string
1372        }
1373        
1374        if ( keys_last ) {
1375          // Complex key, build deep object structure based on a few rules:
1376          // * The 'cur' pointer starts at the object top-level.
1377          // * [] = array push (n is set to array length), [n] = array if n is 
1378          //   numeric, otherwise object.
1379          // * If at the last keys part, set the value.
1380          // * For each keys part, if the current level is undefined create an
1381          //   object or array based on the type of the next keys part.
1382          // * Move the 'cur' pointer to the next level.
1383          // * Rinse & repeat.
1384          for ( ; i <= keys_last; i++ ) {
1385            key = keys[i] === '' ? cur.length : keys[i];
1386            cur = cur[key] = i < keys_last
1387              ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
1388              : val;
1389          }
1390          
1391        } else {
1392          // Simple key, even simpler rules, since only scalars and shallow
1393          // arrays are allowed.
1394          
1395          if ( $.isArray( obj[key] ) ) {
1396            // val is already an array, so push on the next value.
1397            obj[key].push( val );
1398            
1399          } else if ( obj[key] !== undefined ) {
1400            // val isn't an array, but since a second value has been specified,
1401            // convert val into an array.
1402            obj[key] = [ obj[key], val ];
1403            
1404          } else {
1405            // val is a scalar.
1406            obj[key] = val;
1407          }
1408        }
1409        
1410      } else if ( key ) {
1411        // No value was defined, so set something meaningful.
1412        obj[key] = coerce
1413          ? undefined
1414          : '';
1415      }
1416    });
1417    
1418    return obj;
1419  };
1420  
1421  // Method: jQuery.deparam.querystring
1422  // 
1423  // Parse the query string from a URL or the current window.location,
1424  // deserializing it into an object, optionally coercing numbers, booleans,
1425  // null and undefined values.
1426  // 
1427  // Usage:
1428  // 
1429  // > jQuery.deparam.querystring( [ url ] [, coerce ] );
1430  // 
1431  // Arguments:
1432  // 
1433  //  url - (String) An optional params string or URL containing query string
1434  //    params to be parsed. If url is omitted, the current window.location
1435  //    is used.
1436  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
1437  //    undefined to their actual value. Defaults to false if omitted.
1438  // 
1439  // Returns:
1440  // 
1441  //  (Object) An object representing the deserialized params string.
1442  
1443  // Method: jQuery.deparam.fragment
1444  // 
1445  // Parse the fragment (hash) from a URL or the current window.location,
1446  // deserializing it into an object, optionally coercing numbers, booleans,
1447  // null and undefined values.
1448  // 
1449  // Usage:
1450  // 
1451  // > jQuery.deparam.fragment( [ url ] [, coerce ] );
1452  // 
1453  // Arguments:
1454  // 
1455  //  url - (String) An optional params string or URL containing fragment (hash)
1456  //    params to be parsed. If url is omitted, the current window.location
1457  //    is used.
1458  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
1459  //    undefined to their actual value. Defaults to false if omitted.
1460  // 
1461  // Returns:
1462  // 
1463  //  (Object) An object representing the deserialized params string.
1464  
1465  function jq_deparam_sub( is_fragment, url_or_params, coerce ) {
1466    if ( url_or_params === undefined || typeof url_or_params === 'boolean' ) {
1467      // url_or_params not specified.
1468      coerce = url_or_params;
1469      url_or_params = jq_param[ is_fragment ? str_fragment : str_querystring ]();
1470    } else {
1471      url_or_params = is_string( url_or_params )
1472        ? url_or_params.replace( is_fragment ? re_trim_fragment : re_trim_querystring, '' )
1473        : url_or_params;
1474    }
1475    
1476    return jq_deparam( url_or_params, coerce );
1477  };
1478  
1479  jq_deparam[ str_querystring ]                    = curry( jq_deparam_sub, 0 );
1480  jq_deparam[ str_fragment ] = jq_deparam_fragment = curry( jq_deparam_sub, 1 );
1481  
1482  // Section: Element manipulation
1483  // 
1484  // Method: jQuery.elemUrlAttr
1485  // 
1486  // Get the internal "Default URL attribute per tag" list, or augment the list
1487  // with additional tag-attribute pairs, in case the defaults are insufficient.
1488  // 
1489  // In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
1490  // is used to determine which attribute contains the URL to be modified, if
1491  // an "attr" param is not specified.
1492  // 
1493  // Default Tag-Attribute List:
1494  // 
1495  //  a      - href
1496  //  base   - href
1497  //  iframe - src
1498  //  img    - src
1499  //  input  - src
1500  //  form   - action
1501  //  link   - href
1502  //  script - src
1503  // 
1504  // Usage:
1505  // 
1506  // > jQuery.elemUrlAttr( [ tag_attr ] );
1507  // 
1508  // Arguments:
1509  // 
1510  //  tag_attr - (Object) An object containing a list of tag names and their
1511  //    associated default attribute names in the format { tag: 'attr', ... } to
1512  //    be merged into the internal tag-attribute list.
1513  // 
1514  // Returns:
1515  // 
1516  //  (Object) An object containing all stored tag-attribute values.
1517  
1518  // Only define function and set defaults if function doesn't already exist, as
1519  // the urlInternal plugin will provide this method as well.
1520  $[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) {
1521    return $.extend( elemUrlAttr_cache, obj );
1522  })({
1523    a: str_href,
1524    base: str_href,
1525    iframe: str_src,
1526    img: str_src,
1527    input: str_src,
1528    form: 'action',
1529    link: str_href,
1530    script: str_src
1531  });
1532  
1533  jq_elemUrlAttr = $[ str_elemUrlAttr ];
1534  
1535  // Method: jQuery.fn.querystring
1536  // 
1537  // Update URL attribute in one or more elements, merging the current URL (with
1538  // or without pre-existing query string params) plus any params object or
1539  // string into a new URL, which is then set into that attribute. Like
1540  // <jQuery.param.querystring (build url)>, but for all elements in a jQuery
1541  // collection.
1542  // 
1543  // Usage:
1544  // 
1545  // > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
1546  // 
1547  // Arguments:
1548  // 
1549  //  attr - (String) Optional name of an attribute that will contain a URL to
1550  //    merge params or url into. See <jQuery.elemUrlAttr> for a list of default
1551  //    attributes.
1552  //  params - (Object) A params object to be merged into the URL attribute.
1553  //  params - (String) A URL containing query string params, or params string
1554  //    to be merged into the URL attribute.
1555  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
1556  //    specified, and is as-follows:
1557  //    
1558  //    * 0: params in the params argument will override any params in attr URL.
1559  //    * 1: any params in attr URL will override params in the params argument.
1560  //    * 2: params argument will completely replace any query string in attr
1561  //         URL.
1562  // 
1563  // Returns:
1564  // 
1565  //  (jQuery) The initial jQuery collection of elements, but with modified URL
1566  //  attribute values.
1567  
1568  // Method: jQuery.fn.fragment
1569  // 
1570  // Update URL attribute in one or more elements, merging the current URL (with
1571  // or without pre-existing fragment/hash params) plus any params object or
1572  // string into a new URL, which is then set into that attribute. Like
1573  // <jQuery.param.fragment (build url)>, but for all elements in a jQuery
1574  // collection.
1575  // 
1576  // Usage:
1577  // 
1578  // > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
1579  // 
1580  // Arguments:
1581  // 
1582  //  attr - (String) Optional name of an attribute that will contain a URL to
1583  //    merge params into. See <jQuery.elemUrlAttr> for a list of default
1584  //    attributes.
1585  //  params - (Object) A params object to be merged into the URL attribute.
1586  //  params - (String) A URL containing fragment (hash) params, or params
1587  //    string to be merged into the URL attribute.
1588  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
1589  //    specified, and is as-follows:
1590  //    
1591  //    * 0: params in the params argument will override any params in attr URL.
1592  //    * 1: any params in attr URL will override params in the params argument.
1593  //    * 2: params argument will completely replace any fragment (hash) in attr
1594  //         URL.
1595  // 
1596  // Returns:
1597  // 
1598  //  (jQuery) The initial jQuery collection of elements, but with modified URL
1599  //  attribute values.
1600  
1601  function jq_fn_sub( mode, force_attr, params, merge_mode ) {
1602    if ( !is_string( params ) && typeof params !== 'object' ) {
1603      // force_attr not specified.
1604      merge_mode = params;
1605      params = force_attr;
1606      force_attr = undefined;
1607    }
1608    
1609    return this.each(function(){
1610      var that = $(this),
1611        
1612        // Get attribute specified, or default specified via $.elemUrlAttr.
1613        attr = force_attr || jq_elemUrlAttr()[ ( this.nodeName || '' ).toLowerCase() ] || '',
1614        
1615        // Get URL value.
1616        url = attr && that.attr( attr ) || '';
1617      
1618      // Update attribute with new URL.
1619      that.attr( attr, jq_param[ mode ]( url, params, merge_mode ) );
1620    });
1621    
1622  };
1623  
1624  $.fn[ str_querystring ] = curry( jq_fn_sub, str_querystring );
1625  $.fn[ str_fragment ]    = curry( jq_fn_sub, str_fragment );
1626  
1627  // Section: History, hashchange event
1628  // 
1629  // Method: jQuery.bbq.pushState
1630  // 
1631  // Adds a 'state' into the browser history at the current position, setting
1632  // location.hash and triggering any bound <hashchange event> callbacks
1633  // (provided the new state is different than the previous state).
1634  // 
1635  // If no arguments are passed, an empty state is created, which is just a
1636  // shortcut for jQuery.bbq.pushState( {}, 2 ).
1637  // 
1638  // Usage:
1639  // 
1640  // > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
1641  // 
1642  // Arguments:
1643  // 
1644  //  params - (String) A serialized params string or a hash string beginning
1645  //    with # to merge into location.hash.
1646  //  params - (Object) A params object to merge into location.hash.
1647  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
1648  //    specified (unless a hash string beginning with # is specified, in which
1649  //    case merge behavior defaults to 2), and is as-follows:
1650  // 
1651  //    * 0: params in the params argument will override any params in the
1652  //         current state.
1653  //    * 1: any params in the current state will override params in the params
1654  //         argument.
1655  //    * 2: params argument will completely replace current state.
1656  // 
1657  // Returns:
1658  // 
1659  //  Nothing.
1660  // 
1661  // Additional Notes:
1662  // 
1663  //  * Setting an empty state may cause the browser to scroll.
1664  //  * Unlike the fragment and querystring methods, if a hash string beginning
1665  //    with # is specified as the params agrument, merge_mode defaults to 2.
1666  
1667  jq_bbq.pushState = jq_bbq_pushState = function( params, merge_mode ) {
1668    if ( is_string( params ) && /^#/.test( params ) && merge_mode === undefined ) {
1669      // Params string begins with # and merge_mode not specified, so completely
1670      // overwrite window.location.hash.
1671      merge_mode = 2;
1672    }
1673    
1674    var has_args = params !== undefined,
1675      // Merge params into window.location using $.param.fragment.
1676      url = jq_param_fragment( window[ str_location ][ str_href ],
1677        has_args ? params : {}, has_args ? merge_mode : 2 );
1678    
1679    // Set new window.location.href. If hash is empty, use just # to prevent
1680    // browser from reloading the page. Note that Safari 3 & Chrome barf on
1681    // location.hash = '#'.
1682    window[ str_location ][ str_href ] = url + ( /#/.test( url ) ? '' : '#' );
1683  };
1684  
1685  // Method: jQuery.bbq.getState
1686  // 
1687  // Retrieves the current 'state' from the browser history, parsing
1688  // location.hash for a specific key or returning an object containing the
1689  // entire state, optionally coercing numbers, booleans, null and undefined
1690  // values.
1691  // 
1692  // Usage:
1693  // 
1694  // > jQuery.bbq.getState( [ key ] [, coerce ] );
1695  // 
1696  // Arguments:
1697  // 
1698  //  key - (String) An optional state key for which to return a value.
1699  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
1700  //    undefined to their actual value. Defaults to false.
1701  // 
1702  // Returns:
1703  // 
1704  //  (Anything) If key is passed, returns the value corresponding with that key
1705  //    in the location.hash 'state', or undefined. If not, an object
1706  //    representing the entire 'state' is returned.
1707  
1708  jq_bbq.getState = jq_bbq_getState = function( key, coerce ) {
1709    return key === undefined || typeof key === 'boolean'
1710      ? jq_deparam_fragment( key ) // 'key' really means 'coerce' here
1711      : jq_deparam_fragment( coerce )[ key ];
1712  };
1713  
1714  // Method: jQuery.bbq.removeState
1715  // 
1716  // Remove one or more keys from the current browser history 'state', creating
1717  // a new state, setting location.hash and triggering any bound
1718  // <hashchange event> callbacks (provided the new state is different than
1719  // the previous state).
1720  // 
1721  // If no arguments are passed, an empty state is created, which is just a
1722  // shortcut for jQuery.bbq.pushState( {}, 2 ).
1723  // 
1724  // Usage:
1725  // 
1726  // > jQuery.bbq.removeState( [ key [, key ... ] ] );
1727  // 
1728  // Arguments:
1729  // 
1730  //  key - (String) One or more key values to remove from the current state,
1731  //    passed as individual arguments.
1732  //  key - (Array) A single array argument that contains a list of key values
1733  //    to remove from the current state.
1734  // 
1735  // Returns:
1736  // 
1737  //  Nothing.
1738  // 
1739  // Additional Notes:
1740  // 
1741  //  * Setting an empty state may cause the browser to scroll.
1742  
1743  jq_bbq.removeState = function( arr ) {
1744    var state = {};
1745    
1746    // If one or more arguments is passed..
1747    if ( arr !== undefined ) {
1748      
1749      // Get the current state.
1750      state = jq_bbq_getState();
1751      
1752      // For each passed key, delete the corresponding property from the current
1753      // state.
1754      $.each( $.isArray( arr ) ? arr : arguments, function(i,v){
1755        delete state[ v ];
1756      });
1757    }
1758    
1759    // Set the state, completely overriding any existing state.
1760    jq_bbq_pushState( state, 2 );
1761  };
1762  
1763  // Event: hashchange event (BBQ)
1764  // 
1765  // Usage in jQuery 1.4 and newer:
1766  // 
1767  // In jQuery 1.4 and newer, the event object passed into any hashchange event
1768  // callback is augmented with a copy of the location.hash fragment at the time
1769  // the event was triggered as its event.fragment property. In addition, the
1770  // event.getState method operates on this property (instead of location.hash)
1771  // which allows this fragment-as-a-state to be referenced later, even after
1772  // window.location may have changed.
1773  // 
1774  // Note that event.fragment and event.getState are not defined according to
1775  // W3C (or any other) specification, but will still be available whether or
1776  // not the hashchange event exists natively in the browser, because of the
1777  // utility they provide.
1778  // 
1779  // The event.fragment property contains the output of <jQuery.param.fragment>
1780  // and the event.getState method is equivalent to the <jQuery.bbq.getState>
1781  // method.
1782  // 
1783  // > $(window).bind( 'hashchange', function( event ) {
1784  // >   var hash_str = event.fragment,
1785  // >     param_obj = event.getState(),
1786  // >     param_val = event.getState( 'param_name' ),
1787  // >     param_val_coerced = event.getState( 'param_name', true );
1788  // >   ...
1789  // > });
1790  // 
1791  // Usage in jQuery 1.3.2:
1792  // 
1793  // In jQuery 1.3.2, the event object cannot to be augmented as in jQuery 1.4+,
1794  // so the fragment state isn't bound to the event object and must instead be
1795  // parsed using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
1796  // 
1797  // > $(window).bind( 'hashchange', function( event ) {
1798  // >   var hash_str = $.param.fragment(),
1799  // >     param_obj = $.bbq.getState(),
1800  // >     param_val = $.bbq.getState( 'param_name' ),
1801  // >     param_val_coerced = $.bbq.getState( 'param_name', true );
1802  // >   ...
1803  // > });
1804  // 
1805  // Additional Notes:
1806  // 
1807  // * Due to changes in the special events API, jQuery BBQ v1.2 or newer is
1808  //   required to enable the augmented event object in jQuery 1.4.2 and newer.
1809  // * See <jQuery hashchange event> for more detailed information.
1810  
1811  jq_event_special[ str_hashchange ] = $.extend( jq_event_special[ str_hashchange ], {
1812    
1813    // Augmenting the event object with the .fragment property and .getState
1814    // method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
1815    // work, but the event won't be augmented)
1816    add: function( handleObj ) {
1817      var old_handler;
1818      
1819      function new_handler(e) {
1820        // e.fragment is set to the value of location.hash (with any leading #
1821        // removed) at the time the event is triggered.
1822        var hash = e[ str_fragment ] = jq_param_fragment();
1823        
1824        // e.getState() works just like $.bbq.getState(), but uses the
1825        // e.fragment property stored on the event object.
1826        e.getState = function( key, coerce ) {
1827          return key === undefined || typeof key === 'boolean'
1828            ? jq_deparam( hash, key ) // 'key' really means 'coerce' here
1829            : jq_deparam( hash, coerce )[ key ];
1830        };
1831        
1832        old_handler.apply( this, arguments );
1833      };
1834      
1835      // This may seem a little complicated, but it normalizes the special event
1836      // .add method between jQuery 1.4/1.4.1 and 1.4.2+
1837      if ( $.isFunction( handleObj ) ) {
1838        // 1.4, 1.4.1
1839        old_handler = handleObj;
1840        return new_handler;
1841      } else {
1842        // 1.4.2+
1843        old_handler = handleObj.handler;
1844        handleObj.handler = new_handler;
1845      }
1846    }
1847    
1848  });
1849  
1850})(jQuery,this);
1851
1852/*!
1853 * jQuery hashchange event - v1.2 - 2/11/2010
1854 * http://benalman.com/projects/jquery-hashchange-plugin/
1855 * 
1856 * Copyright (c) 2010 "Cowboy" Ben Alman
1857 * Dual licensed under the MIT and GPL licenses.
1858 * http://benalman.com/about/license/
1859 */
1860
1861// Script: jQuery hashchange event
1862//
1863// *Version: 1.2, Last updated: 2/11/2010*
1864// 
1865// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
1866// GitHub       - http://github.com/cowboy/jquery-hashchange/
1867// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
1868// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (1.1kb)
1869// 
1870// About: License
1871// 
1872// Copyright (c) 2010 "Cowboy" Ben Alman,
1873// Dual licensed under the MIT and GPL licenses.
1874// http://benalman.com/about/license/
1875// 
1876// About: Examples
1877// 
1878// This working example, complete with fully commented code, illustrate one way
1879// in which this plugin can be used.
1880// 
1881// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
1882// 
1883// About: Support and Testing
1884// 
1885// Information about what version or versions of jQuery this plugin has been
1886// tested with, what browsers it has been tested in, and where the unit tests
1887// reside (so you can test it yourself).
1888// 
1889// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
1890// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.1.
1891// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
1892// 
1893// About: Known issues
1894// 
1895// While this jQuery hashchange event implementation is quite stable and robust,
1896// there are a few unfortunate browser bugs surrounding expected hashc
1896hange
1897// event-based behaviors, independent of any JavaScript window.onhashchange
1898// abstraction. See the following examples for more information:
1899// 
1900// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
1901// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
1902// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
1903// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
1904// 
1905// About: Release History
1906// 
1907// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
1908//         from a page on another domain would cause an error in Safari 4. Also,
1909//         IE6/7 Iframe is now inserted after the body (this actually works),
1910//         which prevents the page from scrolling when the event is first bound.
1911//         Event can also now be bound before DOM ready, but it won't be usable
1912//         before then in IE6/7.
1913// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
1914//         where browser version is incorrectly reported as 8.0, despite
1915//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
1916// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
1917//         window.onhashchange functionality into a separate plugin for users
1918//         who want just the basic event & back button support, without all the
1919//         extra awesomeness that BBQ provides. This plugin will be included as
1920//         part of jQuery BBQ, but also be available separately.
1921
1922(function($,window,undefined){
1923  '$:nomunge'; // Used by YUI compressor.
1924  
1925  // Method / object references.
1926  var fake_onhashchange,
1927    jq_event_special = $.event.special,
1928    
1929    // Reused strings.
1930    str_location = 'location',
1931    str_hashchange = 'hashchange',
1932    str_href = 'href',
1933    
1934    // IE6/7 specifically need some special love when it comes to back-button
1935    // support, so let's do a little browser sniffing..
1936    browser = $.browser,
1937    mode = document.documentMode,
1938    is_old_ie = browser.msie && ( mode === undefined || mode < 8 ),
1939    
1940    // Does the browser support window.onhashchange? Test for IE version, since
1941    // IE8 incorrectly reports this when in "IE7" or "IE8 Compatibility View"!
1942    supports_onhashchange = 'on' + str_hashchange in window && !is_old_ie;
1943  
1944  // Get location.hash (or what you'd expect location.hash to be) sans any
1945  // leading #. Thanks for making this necessary, Firefox!
1946  function get_fragment( url ) {
1947    url = url || window[ str_location ][ str_href ];
1948    return url.replace( /^[^#]*#?(.*)$/, '$1' );
1949  };
1950  
1951  // Property: jQuery.hashchangeDelay
1952  // 
1953  // The numeric interval (in milliseconds) at which the <hashchange event>
1954  // polling loop executes. Defaults to 100.
1955  
1956  $[ str_hashchange + 'Delay' ] = 100;
1957  
1958  // Event: hashchange event
1959  // 
1960  // Fired when location.hash changes. In browsers that support it, the native
1961  // window.onhashchange event is used (IE8, FF3.6), otherwise a polling loop is
1962  // initialized, running every <jQuery.hashchangeDelay> milliseconds to see if
1963  // the hash has changed. In IE 6 and 7, a hidden Iframe is created to allow
1964  // the back button and hash-based history to work.
1965  // 
1966  // Usage:
1967  // 
1968  // > $(window).bind( 'hashchange', function(e) {
1969  // >   var hash = location.hash;
1970  // >   ...
1971  // > });
1972  // 
1973  // Additional Notes:
1974  // 
1975  // * The polling loop and Iframe are not created until at least one callback
1976  //   is actually bound to 'hashchange'.
1977  // * If you need the bound callback(s) to execute immediately, in cases where
1978  //   the page 'state' exists on page load (via bookmark or page refresh, for
1979  //   example) use $(window).trigger( 'hashchange' );
1980  // * The event can be bound before DOM ready, but since it won't be usable
1981  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
1982  //   to bind it inside a $(document).ready() callback.
1983  
1984  jq_event_special[ str_hashchange ] = $.extend( jq_event_special[ str_hashchange ], {
1985    
1986    // Called only when the first 'hashchange' event is bound to window.
1987    setup: function() {
1988      // If window.onhashchange is supported natively, there's nothing to do..
1989      if ( supports_onhashchange ) { return false; }
1990      
1991      // Otherwise, we need to create our own. And we don't want to call this
1992      // until the user binds to the event, just in case they never do, since it
1993      // will create a polling loop and possibly even a hidden Iframe.
1994      $( fake_onhashchange.start );
1995    },
1996    
1997    // Called only when the last 'hashchange' event is unbound from window.
1998    teardown: function() {
1999      // If window.onhashchange is supported natively, there's nothing to do..
2000      if ( supports_onhashchange ) { return false; }
2001      
2002      // Otherwise, we need to stop ours (if possible).
2003      $( fake_onhashchange.stop );
2004    }
2005    
2006  });
2007  
2008  // fake_onhashchange does all the work of triggering the window.onhashchange
2009  // event for browsers that don't natively support it, including creating a
2010  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
2011  // Iframe to enable back and forward.
2012  fake_onhashchange = (function(){
2013    var self = {},
2014      timeout_id,
2015      iframe,
2016      set_history,
2017      get_history;
2018    
2019    // Initialize. In IE 6/7, creates a hidden Iframe for history handling.
2020    function init(){
2021      // Most browsers don't need special methods here..
2022      set_history = get_history = function(val){ return val; };
2023      
2024      // But IE6/7 do!
2025      if ( is_old_ie ) {
2026        
2027        // Create hidden Iframe after the end of the body to prevent initial
2028        // page load from scrolling unnecessarily.
2029        iframe = $('<iframe src="javascript:0"/>').hide().insertAfter( 'body' )[0].contentWindow;
2030        
2031        // Get history by looking at the hidden Iframe's location.hash.
2032        get_history = function() {
2033          return get_fragment( iframe.document[ str_location ][ str_href ] );
2034        };
2035        
2036        // Set a new history item by opening and then closing the Iframe
2037        // document, *then* setting its location.hash.
2038        set_history = function( hash, history_hash ) {
2039          if ( hash !== history_hash ) {
2040            var doc = iframe.document;
2041            doc.open().close();
2042            doc[ str_location ].hash = '#' + hash;
2043          }
2044        };
2045        
2046        // Set initial history.
2047        set_history( get_fragment() );
2048      }
2049    };
2050    
2051    // Start the polling loop.
2052    self.start = function() {
2053      // Polling loop is already running!
2054      if ( timeout_id ) { return; }
2055      
2056      // Remember the initial hash so it doesn't get triggered immediately.
2057      var last_hash = get_fragment();
2058      
2059      // Initialize if not yet initialized.
2060      set_history || init();
2061      
2062      // This polling loop checks every $.hashchangeDelay milliseconds to see if
2063      // location.hash has changed, and triggers the 'hashchange' event on
2064      // window when necessary.
2065      (function loopy(){
2066        var hash = get_fragment(),
2067          history_hash = get_history( last_hash );
2068        
2069        if ( hash !== last_hash ) {
2070          set_history( last_hash = hash, history_hash );
2071          
2072          $(window).trigger( str_hashchange );
2073          
2074        } else if ( history_hash !== last_hash ) {
2075          window[ str_location ][ str_href ] = window[ str_location ][ str_href ].replace( /#.*/, '' ) + '#' + history_hash;
2076        }
2077        
2078        timeout_id = setTimeout( loopy, $[ str_hashchange + 'Delay' ] );
2079      })();
2080    };
2081    
2082    // Stop the polling loop, but only if an IE6/7 Iframe wasn't created. In
2083    // that case, even if there are no longer any bound event handlers, the
2084    // polling loop is still necessary for back/next to work at all!
2085    self.stop = function() {
2086      if ( !iframe ) {
2087        timeout_id && clearTimeout( timeout_id );
2088        timeout_id = 0;
2089      }
2090    };
2091    
2092    return self;
2093  })();
2094  
2095})(jQuery,this);
2096/*************************************************************************************/
2097
2098/************************** <jStorage-jquery-plugin> *********************************/
2099/*
2100 * ----------------------------- JSTORAGE -------------------------------------
2101 * Simple local storage wrapper to save data on the browser side, supporting
2102 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
2103 *
2104 * Author: Andris Reinman, [email protected]
2105 * Project homepage: www.jstorage.info
2106 *
2107 * Licensed under Unlicense:
2108 *
2109 * This is free and unencumbered software released into the public domain.
2110 *
2111 * Anyone is free to copy, modify, publish, use, compile, sell, or
2112 * distribute this software, either in source code form or as a compiled
2113 * binary, for any purpose, commercial or non-commercial, and by any
2114 * means.
2115 *
2116 * In jurisdictions that recognize copyright laws, the author or authors
2117 * of this software dedicate any and all copyright interest in the
2118 * software to the public domain. We make this dedication for the benefit
2119 * of the public at large and to the detriment of our heirs and
2120 * successors. We intend this dedication to be an overt act of
2121 * relinquishment in perpetuity of all present and future rights to this
2122 * software under copyright law.
2123 *
2124 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2125 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2126 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2127 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2128 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2129 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2130 * OTHER DEALINGS IN THE SOFTWARE.
2131 *
2132 * For more information, please refer to <http://unlicense.org/>
2133 */
2134
2135(function() {
2136    'use strict';
2137
2138    var
2139    /* jStorage version */
2140        JSTORAGE_VERSION = '0.4.11',
2141
2142        /* detect a dollar object or create one if not found */
2143        $ = window.jQuery || window.$ || (window.$ = {}),
2144
2145        /* check for a JSON handling support */
2146        JSON = {
2147            parse: window.JSON && (window.JSON.parse || window.JSON.decode) ||
2148                String.prototype.evalJSON && function(str) {
2149                    return String(str).evalJSON();
2150            } ||
2151                $.parseJSON ||
2152                $.evalJSON,
vendor: 19,510 bytes, lines 2153-2755
2153            stringify: Object.toJSON ||
2154                window.JSON && (window.JSON.stringify || window.JSON.encode) ||
2155                $.toJSON
2156        };
2157
2158    // Break if no JSON support was found
2159    if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') {
2160        throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page');
2161    }
2162
2163    var
2164    /* This is the object, that holds the cached values */
2165        _storage = {
2166            __jstorage_meta: {
2167                CRC32: {}
2168            }
2169        },
2170
2171        /* Actual browser storage (localStorage or globalStorage['domain']) */
2172        _storage_service = {
2173            jStorage: '{}'
2174        },
2175
2176        /* DOM element for older IE versions, holds userData behavior */
2177        _storage_elm = null,
2178
2179        /* How much space does the storage take */
2180        _storage_size = 0,
2181
2182        /* which backend is currently used */
2183        _backend = false,
2184
2185        /* onchange observers */
2186        _observers = {},
2187
2188        /* timeout to wait after onchange event */
2189        _observer_timeout = false,
2190
2191        /* last update time */
2192        _observer_update = 0,
2193
2194        /* pubsub observers */
2195        _pubsub_observers = {},
2196
2197        /* skip published items older than current timestamp */
2198        _pubsub_last = +new Date(),
2199
2200        /* Next check for TTL */
2201        _ttl_timeout,
2202
2203        /**
2204         * XML encoding and decoding as XML nodes can't be JSON'ized
2205         * XML nodes are encoded and decoded if the node is the value to be saved
2206         * but not if it's as a property of another object
2207         * Eg. -
2208         *   $.jStorage.set('key', xmlNode);        // IS OK
2209         *   $.jStorage.set('key', {xml: xmlNode}); // NOT OK
2210         */
2211        _XMLService = {
2212
2213            /**
2214             * Validates a XML node to be XML
2215             * based on jQuery.isXML function
2216             */
2217            isXML: function(elm) {
2218                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
2219                return documentElement ? documentElement.nodeName !== 'HTML' : false;
2220            },
2221
2222            /**
2223             * Encodes a XML node to string
2224             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
2225             */
2226            encode: function(xmlNode) {
2227                if (!this.isXML(xmlNode)) {
2228                    return false;
2229                }
2230                try { // Mozilla, Webkit, Opera
2231                    return new XMLSerializer().serializeToString(xmlNode);
2232                } catch (E1) {
2233                    try { // IE
2234                        return xmlNode.xml;
2235                    } catch (E2) {}
2236                }
2237                return false;
2238            },
2239
2240            /**
2241             * Decodes a XML node from string
2242             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
2243             */
2244            decode: function(xmlString) {
2245                var dom_parser = ('DOMParser' in window && (new DOMParser()).parseFromString) ||
2246                    (window.ActiveXObject && function(_xmlString) {
2247                        var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
2248                        xml_doc.async = 'false';
2249                        xml_doc.loadXML(_xmlString);
2250                        return xml_doc;
2251                    }),
2252                    resultXML;
2253                if (!dom_parser) {
2254                    return false;
2255                }
2256                resultXML = dom_parser.call('DOMParser' in window && (new DOMParser()) || window, xmlString, 'text/xml');
2257                return this.isXML(resultXML) ? resultXML : false;
2258            }
2259        };
2260
2261
2262    ////////////////////////// PRIVATE METHODS ////////////////////////
2263
2264    /**
2265     * Initialization function. Detects if the browser supports DOM Storage
2266     * or userData behavior and behaves accordingly.
2267     */
2268    function _init() {
2269        /* Check if browser supports localStorage */
2270        var localStorageReallyWorks = false;
2271        if ('localStorage' in window) {
2272            try {
2273                window.localStorage.setItem('_tmptest', 'tmpval');
2274                localStorageReallyWorks = true;
2275                window.localStorage.removeItem('_tmptest');
2276            } catch (BogusQuotaExceededErrorOnIos5) {
2277                // Thanks be to iOS5 Private Browsing mode which throws
2278                // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
2279            }
2280        }
2281
2282        if (localStorageReallyWorks) {
2283            try {
2284                if (window.localStorage) {
2285                    _storage_service = window.localStorage;
2286                    _backend = 'localStorage';
2287                    _observer_update = _storage_service.jStorage_update;
2288                }
2289            } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ }
2290        }
2291        /* Check if browser supports globalStorage */
2292        else if ('globalStorage' in window) {
2293            try {
2294                if (window.globalStorage) {
2295                    if (window.location.hostname == 'localhost') {
2296                        _storage_service = window.globalStorage['localhost.localdomain'];
2297                    } else {
2298                        _storage_service = window.globalStorage[window.location.hostname];
2299                    }
2300                    _backend = 'globalStorage';
2301                    _observer_update = _storage_service.jStorage_update;
2302                }
2303            } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ }
2304        }
2305        /* Check if browser supports userData behavior */
2306        else {
2307            _storage_elm = document.createElement('link');
2308            if (_storage_elm.addBehavior) {
2309
2310                /* Use a DOM element to act as userData storage */
2311                _storage_elm.style.behavior = 'url(#default#userData)';
2312
2313                /* userData element needs to be inserted into the DOM! */
2314                document.getElementsByTagName('head')[0].appendChild(_storage_elm);
2315
2316                try {
2317                    _storage_elm.load('jStorage');
2318                } catch (E) {
2319                    // try to reset cache
2320                    _storage_elm.setAttribute('jStorage', '{}');
2321                    _storage_elm.save('jStorage');
2322                    _storage_elm.load('jStorage');
2323                }
2324
2325                var data = '{}';
2326                try {
2327                    data = _storage_elm.getAttribute('jStorage');
2328                } catch (E5) {}
2329
2330                try {
2331                    _observer_update = _storage_elm.getAttribute('jStorage_update');
2332                } catch (E6) {}
2333
2334                _storage_service.jStorage = data;
2335                _backend = 'userDataBehavior';
2336            } else {
2337                _storage_elm = null;
2338                return;
2339            }
2340        }
2341
2342        // Load data from storage
2343        _load_storage();
2344
2345        // remove dead keys
2346        _handleTTL();
2347
2348        // start listening for changes
2349        _setupObserver();
2350
2351        // initialize publish-subscribe service
2352        _handlePubSub();
2353
2354        // handle cached navigation
2355        if ('addEventListener' in window) {
2356            window.addEventListener('pageshow', function(event) {
2357                if (event.persisted) {
2358                    _storageObserver();
2359                }
2360            }, false);
2361        }
2362    }
2363
2364    /**
2365     * Reload data from storage when needed
2366     */
2367    function _reloadData() {
2368        var data = '{}';
2369
2370        if (_backend == 'userDataBehavior') {
2371            _storage_elm.load('jStorage');
2372
2373            try {
2374                data = _storage_elm.getAttribute('jStorage');
2375            } catch (E5) {}
2376
2377            try {
2378                _observer_update = _storage_elm.getAttribute('jStorage_update');
2379            } catch (E6) {}
2380
2381            _storage_service.jStorage = data;
2382        }
2383
2384        _load_storage();
2385
2386        // remove dead keys
2387        _handleTTL();
2388
2389        _handlePubSub();
2390    }
2391
2392    /**
2393     * Sets up a storage change observer
2394     */
2395    function _setupObserver() {
2396        if (_backend == 'localStorage' || _backend == 'globalStorage') {
2397            if ('addEventListener' in window) {
2398                window.addEventListener('storage', _storageObserver, false);
2399            } else {
2400                document.attachEvent('onstorage', _storageObserver);
2401            }
2402        } else if (_backend == 'userDataBehavior') {
2403            setInterval(_storageObserver, 1000);
2404        }
2405    }
2406
2407    /**
2408     * Fired on any kind of data change, needs to check if anything has
2409     * really been changed
2410     */
2411    function _storageObserver() {
2412        var updateTime;
2413        // cumulate change notifications with timeout
2414        clearTimeout(_observer_timeout);
2415        _observer_timeout = setTimeout(function() {
2416
2417            if (_backend == 'localStorage' || _backend == 'globalStorage') {
2418                updateTime = _storage_service.jStorage_update;
2419            } else if (_backend == 'userDataBehavior') {
2420                _storage_elm.load('jStorage');
2421                try {
2422                    updateTime = _storage_elm.getAttribute('jStorage_update');
2423                } catch (E5) {}
2424            }
2425
2426            if (updateTime && updateTime != _observer_update) {
2427                _observer_update = updateTime;
2428                _checkUpdatedKeys();
2429            }
2430
2431        }, 25);
2432    }
2433
2434    /**
2435     * Reloads the data and checks if any keys are changed
2436     */
2437    function _checkUpdatedKeys() {
2438        var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),
2439            newCrc32List;
2440
2441        _reloadData();
2442        newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));
2443
2444        var key,
2445            updated = [],
2446            removed = [];
2447
2448        for (key in oldCrc32List) {
2449            if (oldCrc32List.hasOwnProperty(key)) {
2450                if (!newCrc32List[key]) {
2451                    removed.push(key);
2452                    continue;
2453                }
2454                if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == '2.') {
2455                    updated.push(key);
2456                }
2457            }
2458        }
2459
2460        for (key in newCrc32List) {
2461            if (newCrc32List.hasOwnProperty(key)) {
2462                if (!oldCrc32List[key]) {
2463                    updated.push(key);
2464                }
2465            }
2466        }
2467
2468        _fireObservers(updated, 'updated');
2469        _fireObservers(removed, 'deleted');
2470    }
2471
2472    /**
2473     * Fires observers for updated keys
2474     *
2475     * @param {Array|String} keys Array of key names or a key
2476     * @param {String} action What happened with the value (updated, deleted, flushed)
2477     */
2478    function _fireObservers(keys, action) {
2479        keys = [].concat(keys || []);
2480
2481        var i, j, len, jlen;
2482
2483        if (action == 'flushed') {
2484            keys = [];
2485            for (var key in _observers) {
2486                if (_observers.hasOwnProperty(key)) {
2487                    keys.push(key);
2488                }
2489            }
2490            action = 'deleted';
2491        }
2492        for (i = 0, len = keys.length; i < len; i++) {
2493            if (_observers[keys[i]]) {
2494                for (j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) {
2495                    _observers[keys[i]][j](keys[i], action);
2496                }
2497            }
2498            if (_observers['*']) {
2499                for (j = 0, jlen = _observers['*'].length; j < jlen; j++) {
2500                    _observers['*'][j](keys[i], action);
2501                }
2502            }
2503        }
2504    }
2505
2506    /**
2507     * Publishes key change to listeners
2508     */
2509    function _publishChange() {
2510        var updateTime = (+new Date()).toString();
2511
2512        if (_backend == 'localStorage' || _backend == 'globalStorage') {
2513            try {
2514                _storage_service.jStorage_update = updateTime;
2515            } catch (E8) {
2516                // safari private mode has been enabled after the jStorage initialization
2517                _backend = false;
2518            }
2519        } else if (_backend == 'userDataBehavior') {
2520            _storage_elm.setAttribute('jStorage_update', updateTime);
2521            _storage_elm.save('jStorage');
2522        }
2523
2524        _storageObserver();
2525    }
2526
2527    /**
2528     * Loads the data from the storage based on the supported mechanism
2529     */
2530    function _load_storage() {
2531        /* if jStorage string is retrieved, then decode it */
2532        if (_storage_service.jStorage) {
2533            try {
2534                _storage = JSON.parse(String(_storage_service.jStorage));
2535            } catch (E6) {
2536                _storage_service.jStorage = '{}';
2537            }
2538        } else {
2539            _storage_service.jStorage = '{}';
2540        }
2541        _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
2542
2543        if (!_storage.__jstorage_meta) {
2544            _storage.__jstorage_meta = {};
2545        }
2546        if (!_storage.__jstorage_meta.CRC32) {
2547            _storage.__jstorage_meta.CRC32 = {};
2548        }
2549    }
2550
2551    /**
2552     * This functions provides the 'save' mechanism to store the jStorage object
2553     */
2554    function _save() {
2555        _dropOldEvents(); // remove expired events
2556        try {
2557            _storage_service.jStorage = JSON.stringify(_storage);
2558            // If userData is used as the storage engine, additional
2559            if (_storage_elm) {
2560                _storage_elm.setAttribute('jStorage', _storage_service.jStorage);
2561                _storage_elm.save('jStorage');
2562            }
2563            _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
2564        } catch (E7) { /* probably cache is full, nothing is saved this way*/ }
2565    }
2566
2567    /**
2568     * Function checks if a key is set and is string or numberic
2569     *
2570     * @param {String} key Key name
2571     */
2572    function _checkKey(key) {
2573        if (typeof key != 'string' && typeof key != 'number') {
2574            throw new TypeError('Key name must be string or numeric');
2575        }
2576        if (key == '__jstorage_meta') {
2577            throw new TypeError('Reserved key name');
2578        }
2579        return true;
2580    }
2581
2582    /**
2583     * Removes expired keys
2584     */
2585    function _handleTTL() {
2586        var curtime, i, TTL, CRC32, nextExpire = Infinity,
2587            changed = false,
2588            deleted = [];
2589
2590        clearTimeout(_ttl_timeout);
2591
2592        if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != 'object') {
2593            // nothing to do here
2594            return;
2595        }
2596
2597        curtime = +new Date();
2598        TTL = _storage.__jstorage_meta.TTL;
2599
2600        CRC32 = _storage.__jstorage_meta.CRC32;
2601        for (i in TTL) {
2602            if (TTL.hasOwnProperty(i)) {
2603                if (TTL[i] <= curtime) {
2604                    delete TTL[i];
2605                    delete CRC32[i];
2606                    delete _storage[i];
2607                    changed = true;
2608                    deleted.push(i);
2609                } else if (TTL[i] < nextExpire) {
2610                    nextExpire = TTL[i];
2611                }
2612            }
2613        }
2614
2615        // set next check
2616        if (nextExpire != Infinity) {
2617            _ttl_timeout = setTimeout(_handleTTL, Math.min(nextExpire - curtime, 0x7FFFFFFF));
2618        }
2619
2620        // save changes
2621        if (changed) {
2622            _save();
2623            _publishChange();
2624            _fireObservers(deleted, 'deleted');
2625        }
2626    }
2627
2628    /**
2629     * Checks if there's any events on hold to be fired to listeners
2630     */
2631    function _handlePubSub() {
2632        var i, len;
2633        if (!_storage.__jstorage_meta.PubSub) {
2634            return;
2635        }
2636        var pubelm,
2637            _pubsubCurrent = _pubsub_last;
2638
2639        for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) {
2640            pubelm = _storage.__jstorage_meta.PubSub[i];
2641            if (pubelm[0] > _pubsub_last) {
2642                _pubsubCurrent = pubelm[0];
2643                _fireSubscribers(pubelm[1], pubelm[2]);
2644            }
2645        }
2646
2647        _pubsub_last = _pubsubCurrent;
2648    }
2649
2650    /**
2651     * Fires all subscriber listeners for a pubsub channel
2652     *
2653     * @param {String} channel Channel name
2654     * @param {Mixed} payload Payload data to deliver
2655     */
2656    function _fireSubscribers(channel, payload) {
2657        if (_pubsub_observers[channel]) {
2658            for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) {
2659                // send immutable data that can't be modified by listeners
2660                try {
2661                    _pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload)));
2662                } catch (E) {}
2663            }
2664        }
2665    }
2666
2667    /**
2668     * Remove old events from the publish stream (at least 2sec old)
2669     */
2670    function _dropOldEvents() {
2671        if (!_storage.__jstorage_meta.PubSub) {
2672            return;
2673        }
2674
2675        var retire = +new Date() - 2000;
2676
2677        for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) {
2678            if (_storage.__jstorage_meta.PubSub[i][0] <= retire) {
2679                // deleteCount is needed for IE6
2680                _storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i);
2681                break;
2682            }
2683        }
2684
2685        if (!_storage.__jstorage_meta.PubSub.length) {
2686            delete _storage.__jstorage_meta.PubSub;
2687        }
2688
2689    }
2690
2691    /**
2692     * Publish payload to a channel
2693     *
2694     * @param {String} channel Channel name
2695     * @param {Mixed} payload Payload to send to the subscribers
2696     */
2697    function _publish(channel, payload) {
2698        if (!_storage.__jstorage_meta) {
2699            _storage.__jstorage_meta = {};
2700        }
2701        if (!_storage.__jstorage_meta.PubSub) {
2702            _storage.__jstorage_meta.PubSub = [];
2703        }
2704
2705        _storage.__jstorage_meta.PubSub.unshift([+new Date(), channel, payload]);
2706
2707        _save();
2708        _publishChange();
2709    }
2710
2711
2712    /**
2713     * JS Implementation of MurmurHash2
2714     *
2715     *  SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed)
2716     *
2717     * @author <a href='mailto:[email protected]'>Gary Court</a>
2718     * @see http://github.com/garycourt/murmurhash-js
2719     * @author <a href='mailto:[email protected]'>Austin Appleby</a>
2720     * @see http://sites.google.com/site/murmurhash/
2721     *
2722     * @param {string} str ASCII only
2723     * @param {number} seed Positive integer only
2724     * @return {number} 32-bit positive integer hash
2725     */
2726
2727    function murmurhash2_32_gc(str, seed) {
2728        var
2729            l = str.length,
2730            h = seed ^ l,
2731            i = 0,
2732            k;
2733
2734        while (l >= 4) {
2735            k =
2736                ((str.charCodeAt(i) & 0xff)) |
2737                ((str.charCodeAt(++i) & 0xff) << 8) |
2738                ((str.charCodeAt(++i) & 0xff) << 16) |
2739                ((str.charCodeAt(++i) & 0xff) << 24);
2740
2741            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
2742            k ^= k >>> 24;
2743            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
2744
2745            h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
2746
2747            l -= 4;
2748            ++i;
2749        }
2750
2751        switch (l) {
2752            case 3:
2753                h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
2754            case 2:
2755                h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
vendor: 9,940 bytes, lines 2755-3084
2755
2756            case 1:
2757                h ^= (str.charCodeAt(i) & 0xff);
2758                h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
2759        }
2760
2761        h ^= h >>> 13;
2762        h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
2763        h ^= h >>> 15;
2764
2765        return h >>> 0;
2766    }
2767
2768    ////////////////////////// PUBLIC INTERFACE /////////////////////////
2769
2770    $.jStorage = {
2771        /* Version number */
2772        version: JSTORAGE_VERSION,
2773
2774        /**
2775         * Sets a key's value.
2776         *
2777         * @param {String} key Key to set. If this value is not set or not
2778         *              a string an exception is raised.
2779         * @param {Mixed} value Value to set. This can be any value that is JSON
2780         *              compatible (Numbers, Strings, Objects etc.).
2781         * @param {Object} [options] - possible options to use
2782         * @param {Number} [options.TTL] - optional TTL value, in milliseconds
2783         * @return {Mixed} the used value
2784         */
2785        set: function(key, value, options) {
2786            _checkKey(key);
2787
2788            options = options || {};
2789
2790            // undefined values are deleted automatically
2791            if (typeof value == 'undefined') {
2792                this.deleteKey(key);
2793                return value;
2794            }
2795
2796            if (_XMLService.isXML(value)) {
2797                value = {
2798                    _is_xml: true,
2799                    xml: _XMLService.encode(value)
2800                };
2801            } else if (typeof value == 'function') {
2802                return undefined; // functions can't be saved!
2803            } else if (value && typeof value == 'object') {
2804                // clone the object before saving to _storage tree
2805                value = JSON.parse(JSON.stringify(value));
2806            }
2807
2808            _storage[key] = value;
2809
2810            _storage.__jstorage_meta.CRC32[key] = '2.' + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c);
2811
2812            this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange
2813
2814            _fireObservers(key, 'updated');
2815            return value;
2816        },
2817
2818        /**
2819         * Looks up a key in cache
2820         *
2821         * @param {String} key - Key to look up.
2822         * @param {mixed} def - Default value to return, if key didn't exist.
2823         * @return {Mixed} the key value, default value or null
2824         */
2825        get: function(key, def) {
2826            _checkKey(key);
2827            if (key in _storage) {
2828                if (_storage[key] && typeof _storage[key] == 'object' && _storage[key]._is_xml) {
2829                    return _XMLService.decode(_storage[key].xml);
2830                } else {
2831                    return _storage[key];
2832                }
2833            }
2834            return typeof(def) == 'undefined' ? null : def;
2835        },
2836
2837        /**
2838         * Deletes a key from cache.
2839         *
2840         * @param {String} key - Key to delete.
2841         * @return {Boolean} true if key existed or false if it didn't
2842         */
2843        deleteKey: function(key) {
2844            _checkKey(key);
2845            if (key in _storage) {
2846                delete _storage[key];
2847                // remove from TTL list
2848                if (typeof _storage.__jstorage_meta.TTL == 'object' &&
2849                    key in _storage.__jstorage_meta.TTL) {
2850                    delete _storage.__jstorage_meta.TTL[key];
2851                }
2852
2853                delete _storage.__jstorage_meta.CRC32[key];
2854
2855                _save();
2856                _publishChange();
2857                _fireObservers(key, 'deleted');
2858                return true;
2859            }
2860            return false;
2861        },
2862
2863        /**
2864         * Sets a TTL for a key, or remove it if ttl value is 0 or below
2865         *
2866         * @param {String} key - key to set the TTL for
2867         * @param {Number} ttl - TTL timeout in milliseconds
2868         * @return {Boolean} true if key existed or false if it didn't
2869         */
2870        setTTL: function(key, ttl) {
2871            var curtime = +new Date();
2872            _checkKey(key);
2873            ttl = Number(ttl) || 0;
2874            if (key in _storage) {
2875
2876                if (!_storage.__jstorage_meta.TTL) {
2877                    _storage.__jstorage_meta.TTL = {};
2878                }
2879
2880                // Set TTL value for the key
2881                if (ttl > 0) {
2882                    _storage.__jstorage_meta.TTL[key] = curtime + ttl;
2883                } else {
2884                    delete _storage.__jstorage_meta.TTL[key];
2885                }
2886
2887                _save();
2888
2889                _handleTTL();
2890
2891                _publishChange();
2892                return true;
2893            }
2894            return false;
2895        },
2896
2897        /**
2898         * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set
2899         *
2900         * @param {String} key Key to check
2901         * @return {Number} Remaining TTL in milliseconds
2902         */
2903        getTTL: function(key) {
2904            var curtime = +new Date(),
2905                ttl;
2906            _checkKey(key);
2907            if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) {
2908                ttl = _storage.__jstorage_meta.TTL[key] - curtime;
2909                return ttl || 0;
2910            }
2911            return 0;
2912        },
2913
2914        /**
2915         * Deletes everything in cache.
2916         *
2917         * @return {Boolean} Always true
2918         */
2919        flush: function() {
2920            _storage = {
2921                __jstorage_meta: {
2922                    CRC32: {}
2923                }
2924            };
2925            _save();
2926            _publishChange();
2927            _fireObservers(null, 'flushed');
2928            return true;
2929        },
2930
2931        /**
2932         * Returns a read-only copy of _storage
2933         *
2934         * @return {Object} Read-only copy of _storage
2935         */
2936        storageObj: function() {
2937            function F() {}
2938            F.prototype = _storage;
2939            return new F();
2940        },
2941
2942        /**
2943         * Returns an index of all used keys as an array
2944         * ['key1', 'key2',..'keyN']
2945         *
2946         * @return {Array} Used keys
2947         */
2948        index: function() {
2949            var index = [],
2950                i;
2951            for (i in _storage) {
2952                if (_storage.hasOwnProperty(i) && i != '__jstorage_meta') {
2953                    index.push(i);
2954                }
2955            }
2956            return index;
2957        },
2958
2959        /**
2960         * How much space in bytes does the storage take?
2961         *
2962         * @return {Number} Storage size in chars (not the same as in bytes,
2963         *                  since some chars may take several bytes)
2964         */
2965        storageSize: function() {
2966            return _storage_size;
2967        },
2968
2969        /**
2970         * Which backend is currently in use?
2971         *
2972         * @return {String} Backend name
2973         */
2974        currentBackend: function() {
2975            return _backend;
2976        },
2977
2978        /**
2979         * Test if storage is available
2980         *
2981         * @return {Boolean} True if storage can be used
2982         */
2983        storageAvailable: function() {
2984            return !!_backend;
2985        },
2986
2987        /**
2988         * Register change listeners
2989         *
2990         * @param {String} key Key name
2991         * @param {Function} callback Function to run when the key changes
2992         */
2993        listenKeyChange: function(key, callback) {
2994            _checkKey(key);
2995            if (!_observers[key]) {
2996                _observers[key] = [];
2997            }
2998            _observers[key].push(callback);
2999        },
3000
3001        /**
3002         * Remove change listeners
3003         *
3004         * @param {String} key Key name to unregister listeners against
3005         * @param {Function} [callback] If set, unregister the callback, if not - unregister all
3006         */
3007        stopListening: function(key, callback) {
3008            _checkKey(key);
3009
3010            if (!_observers[key]) {
3011                return;
3012            }
3013
3014            if (!callback) {
3015                delete _observers[key];
3016                return;
3017            }
3018
3019            for (var i = _observers[key].length - 1; i >= 0; i--) {
3020                if (_observers[key][i] == callback) {
3021                    _observers[key].splice(i, 1);
3022                }
3023            }
3024        },
3025
3026        /**
3027         * Subscribe to a Publish/Subscribe event stream
3028         *
3029         * @param {String} channel Channel name
3030         * @param {Function} callback Function to run when the something is published to the channel
3031         */
3032        subscribe: function(channel, callback) {
3033            channel = (channel || '').toString();
3034            if (!channel) {
3035                throw new TypeError('Channel not defined');
3036            }
3037            if (!_pubsub_observers[channel]) {
3038                _pubsub_observers[channel] = [];
3039            }
3040            _pubsub_observers[channel].push(callback);
3041        },
3042
3043        /**
3044         * Publish data to an event stream
3045         *
3046         * @param {String} channel Channel name
3047         * @param {Mixed} payload Payload to deliver
3048         */
3049        publish: function(channel, payload) {
3050            channel = (channel || '').toString();
3051            if (!channel) {
3052                throw new TypeError('Channel not defined');
3053            }
3054
3055            _publish(channel, payload);
3056        },
3057
3058        /**
3059         * Reloads the data from browser storage
3060         */
3061        reInit: function() {
3062            _reloadData();
3063        },
3064
3065        /**
3066         * Removes reference from global objects and saves it as jStorage
3067         *
3068         * @param {Boolean} option if needed to save object as simple 'jStorage' in windows context
3069         */
3070        noConflict: function(saveInGlobal) {
3071            delete window.$.jStorage;
3072
3073            if (saveInGlobal) {
3074                window.jStorage = this;
3075            }
3076
3077            return this;
3078        }
3079    };
3080
3081    // Initialize jStorage
3082    _init();
3083
3084})();
vendor: 6,162 bytes, lines 3084-3265
3084
3085/************************** </jStorage-jquery-plugin> ********************************/
3086
3087/************************** <.toISOString() shim for IE8> ********************************/
3088if ( !Date.prototype.toISOString ) {
3089
3090    ( function() {
3091
3092        function pad(number) {
3093            var r = String(number);
3094            if ( r.length === 1 ) {
3095                r = '0' + r;
3096            }
3097            return r;
3098        }
3099
3100        Date.prototype.toISOString = function() {
3101            return this.getUTCFullYear()
3102                + '-' + pad( this.getUTCMonth() + 1 )
3103                + '-' + pad( this.getUTCDate() )
3104                + 'T' + pad( this.getUTCHours() )
3105                + ':' + pad( this.getUTCMinutes() )
3106                + ':' + pad( this.getUTCSeconds() )
3107                + '.' + String( (this.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 )
3108                + 'Z';
3109        };
3110
3111    }() );
3112}
3113/************************** </.toISOString() shim for IE8> ********************************/
3114
3115/************************** <http://timeago.yarp.com/jquery.timeago.js> ********************************/
3116/**
3117 * Timeago is a jQuery plugin that makes it easy to support automatically
3118 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
3119 *
3120 * @name timeago
3121 * @version 1.4.1
3122 * @requires jQuery v1.2.3+
3123 * @author Ryan McGeary
3124 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
3125 *
3126 * For usage and examples, visit:
3127 * http://timeago.yarp.com/
3128 *
3129 * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
3130 */
3131
3132(function (factory) {
3133  if (typeof define === 'function' && define.amd) {
3134    // AMD. Register as an anonymous module.
3135    define(['jquery'], factory);
3136  } else {
3137    // Browser globals
3138    factory(jQuery);
3139  }
3140}(function ($) {
3141  $.timeago = function(timestamp) {
3142    if (timestamp instanceof Date) {
3143      return inWords(timestamp);
3144    } else if (typeof timestamp === "string") {
3145      return inWords($.timeago.parse(timestamp));
3146    } else if (typeof timestamp === "number") {
3147      return inWords(new Date(timestamp));
3148    } else {
3149      return inWords($.timeago.datetime(timestamp));
3150    }
3151  };
3152  var $t = $.timeago;
3153
3154  $.extend($.timeago, {
3155    settings: {
3156      refreshMillis: 60000,
3157      allowPast: true,
3158      allowFuture: false,
3159      localeTitle: false,
3160      cutoff: 0,
3161      strings: {
3162        prefixAgo: null,
3163        prefixFromNow: null,
3164        suffixAgo: "ago",
3165        suffixFromNow: "from now",
3166        inPast: 'any moment now',
3167        seconds: "less than a minute",
3168        minute: "about a minute",
3169        minutes: "%d minutes",
3170        hour: "about an hour",
3171        hours: "about %d hours",
3172        day: "a day",
3173        days: "%d days",
3174        month: "about a month",
3175        months: "%d months",
3176        year: "about a year",
3177        years: "%d years",
3178        wordSeparator: " ",
3179        numbers: []
3180      }
3181    },
3182
3183    inWords: function(distanceMillis) {
3184      if(!this.settings.allowPast && ! this.settings.allowFuture) {
3185          throw 'timeago allowPast and allowFuture settings can not both be set to false.';
3186      }
3187
3188      var $l = this.settings.strings;
3189      var prefix = $l.prefixAgo;
3190      var suffix = $l.suffixAgo;
3191      if (this.settings.allowFuture) {
3192        if (distanceMillis < 0) {
3193          prefix = $l.prefixFromNow;
3194          suffix = $l.suffixFromNow;
3195        }
3196      }
3197
3198      if(!this.settings.allowPast && distanceMillis >= 0) {
3199        return this.settings.strings.inPast;
3200      }
3201
3202      var seconds = Math.abs(distanceMillis) / 1000;
3203      var minutes = seconds / 60;
3204      var hours = minutes / 60;
3205      var days = hours / 24;
3206      var years = days / 365;
3207
3208      function substitute(stringOrFunction, number) {
3209        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
3210        var value = ($l.numbers && $l.numbers[number]) || number;
3211        return string.replace(/%d/i, value);
3212      }
3213
3214      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
3215        seconds < 90 && substitute($l.minute, 1) ||
3216        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
3217        minutes < 90 && substitute($l.hour, 1) ||
3218        hours < 24 && substitute($l.hours, Math.round(hours)) ||
3219        hours < 42 && substitute($l.day, 1) ||
3220        days < 30 && substitute($l.days, Math.round(days)) ||
3221        days < 45 && substitute($l.month, 1) ||
3222        days < 365 && substitute($l.months, Math.round(days / 30)) ||
3223        years < 1.5 && substitute($l.year, 1) ||
3224        substitute($l.years, Math.round(years));
3225
3226      var separator = $l.wordSeparator || "";
3227      if ($l.wordSeparator === undefined) { separator = " "; }
3228      return $.trim([prefix, words, suffix].join(separator));
3229    },
3230
3231    parse: function(iso8601) {
3232      var s = $.trim(iso8601);
3233      s = s.replace(/\.\d+/,""); // remove milliseconds
3234      s = s.replace(/-/,"/").replace(/-/,"/");
3235      s = s.replace(/T/," ").replace(/Z/," UTC");
3236      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
3237      s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
3238      return new Date(s);
3239    },
3240    datetime: function(elem) {
3241      var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
3242      return $t.parse(iso8601);
3243    },
3244    isTime: function(elem) {
3245      // jQuery's `is()` doesn't play well with HTML5 in IE
3246      return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
3247    }
3248  });
3249
3250  // functions that can be called via $(el).timeago('action')
3251  // init is default when no action is given
3252  // functions are called with context of a single element
3253  var functions = {
3254    init: function(){
3255      var refresh_el = $.proxy(refresh, this);
3256      refresh_el();
3257      var $s = $t.settings;
3258      if ($s.refreshMillis > 0) {
3259        this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
3260      }
3261    },
3262    update: function(time){
3263      var parsedTime = $t.parse(time);
3264      $(this).data('timeago', { datetime: parsedTime });
3265      if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleStr
3265ing());
3266      refresh.apply(this);
3267    },
3268    updateFromDOM: function(){
3269      $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
3270      refresh.apply(this);
3271    },
3272    dispose: function () {
3273      if (this._timeagoInterval) {
3274        window.clearInterval(this._timeagoInterval);
3275        this._timeagoInterval = null;
3276      }
3277    }
3278  };
3279
3280  $.fn.timeago = function(action, options) {
3281    var fn = action ? functions[action] : functions.init;
3282    if(!fn){
3283      throw new Error("Unknown function name '"+ action +"' for timeago");
3284    }
3285    // each over objects here and call the requested function
3286    this.each(function(){
3287      fn.call(this, options);
3288    });
3289    return this;
3290  };
3291
3292  function refresh() {
3293    var data = prepareData(this);
3294    var $s = $t.settings;
3295
3296    if (!isNaN(data.datetime)) {
3297      if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
3298        $(this).text(inWords(data.datetime));
3299      }
3300    }
3301    return this;
3302  }
3303
3304  function prepareData(element) {
3305    element = $(element);
3306    if (!element.data("timeago")) {
3307      element.data("timeago", { datetime: $t.datetime(element) });
3308      var text = $.trim(element.text());
3309      if ($t.settings.localeTitle) {
3310        element.attr("title", element.data('timeago').datetime.toLocaleString());
3311      } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
3312        element.attr("title", text);
3313      }
3314    }
3315    return element.data("timeago");
3316  }
3317
3318  function inWords(date) {
3319    return $t.inWords(distance(date));
3320  }
3321
3322  function distance(date) {
3323    return (new Date().getTime() - date.getTime());
3324  }
3325
3326  // fix for IE6 suckage
3327  document.createElement("abbr");
3328  document.createElement("time");
3329}));
3330/************************** </http://timeago.yarp.com/jquery.timeago.js> ********************************/
3331
3332$( document ).ready(function() {
3333
3334    new DALAutoComplete({
3335        "count": 5,
3336        "searchDelay": 1000,
3337        "url":"/projectdata",
3338        "textBoxSelector": "#search-query",
3339        "ds": "OQPC-BrowseDataSource",
3340        "collections": ["OQPC.toc"],
3341        "subsets": ["link"],
3342        "renderFunc": function(rec) {
3343            return rec.title.__value__.replace(/^ +| +$/, "");;
3344        },
3345        "textFunc": function(rec) {
3346            return rec.title.__value__.replace(/^ +| +$/, "");;
3347        },
3348        "searchFunc": function(text) {
3349            if(text == "") {
3350                return "";
3351            }
3352            this.expression = '(title=(?"' + text.split(" ").join('"? AND ?"') +
3353                              '"?) AND ((PitValid=@pointInTime(' + DALCore.JsDateToString(new Date(), "yymmddhhiiss") + ')' +
3354                              'AND (PrintType="act.reprint" OR PrintType="reprint")) OR ' +
3355                              '(PrintType="bill.first" OR PrintType="bill.third" OR ' + getRenditionPrintTypeQuery() + ')))';
3356            return "?";
3357        },
3358        "selectFunc": function(rec) {
3359            window.location = createUrl(rec);
3360        },
3361        "sortField" : ["print.type","sort.title"]   // Sort by print.type then by sort.title
3362    });
3363    
3364    //var qldGovSearch = "https://find.search.qld.gov.au/s/search.html?collection=qld-gov&profile=qld";
3365    
3366    //Load stored font size
3367	if ($.jStorage.get("currFontSize") != null) {
3368        var fs = $.jStorage.get("currFontSize") + "%";
3369        $("#main").css("font-size", fs);
3370        if(fs == (g_minF + "%")) {
3371        	$("#resizeDown").prop("disabled", true);
3372        }
3373        else if(fs == (g_maxF + "%")) {
3374        	$("#resizeUp").prop("disabled", true);
3375        }
3376    }
3377    else {
3378    	$.jStorage.set("currFontSize", g_minF);
3379    	$("#resizeDown").prop("disabled", true);
3380    }
3381    
3382    $("#search-form").submit(function(e){
3383        e.preventDefault();
3384        var query = $("#search-query").val();
3385        //var query = $("#search-query").val().replace(/\s\s+/g, '+');
3386        if(window.location.pathname.match(/\/search/)) {
3387            $('#basic-search-form input[name=searchfor]').val(query);
3388            window.quicksearch = true;
3389            $("#basic-search-form").submit();
3390        } else {
3391            window.location = window.location.origin + "/search/inforce#?action=quicksearch&query=" + encodeURIComponent(query);
3392            //window.location = qldGovSearch + "&query=" + encodeURI(query);
3393    }
3394	});
3395
3396    //Mark currently active URL
3397    $("a[href]").each(function() {
3398    if (this.href == window.location.href) {
3399        $(this).addClass("currentPage");
3400        }
3401    });
3402    
3403    $('.btn-tooltip').tooltip();
3404    $('.bs-popover').popover({ trigger: "hover" });
3405    
3406    var offset = 220;
3407    var duration = 100;
3408    
3409    $(window).scroll(function() {
3410	        if ($(this).scrollTop() > offset) {
3411	            $('#top-link-block').fadeIn(duration);
3412	        } else {
3413	            $('#top-link-block').fadeOut(duration);
3414	        }
3415    });
3416    
3417    $('#top-link-block').click(function(event) {
3418        event.preventDefault();
3419        $('html, body').animate({scrollTop: 0}, duration);
3420        return false;
3421    });
3422    
3423    /*Just this has to be replaced in top.ace*/
3424    if($.cookie("logged-on") === "true")
3425    {
3426        $('.logout').removeClass('display-none');
3427        $('.logout').addClass('display-inline-block');
3428    }
3429    else
3430    {
3431    	$('#mobileLogout').removeClass('display-inline-block');
3432    	$('#mobileLogout').addClass('display-none');
3433    }
3434
3435    // Bind print events
3436    $("#access-toolbar > button[data-original-title=Print], #access-toolbar > button[title=Print]").click(function() {
3437        window.print();
3438        return false;
3439    });
3440});
3441
3442var getRenditionPrintTypeExtensions = function() {
3443    return [
3444        "exp",
3445        "risras",
3446        "err",
3447        "hrc",
3448        "hrcerr"
3449    ];
3450}
3451// Don't forget to check the getCollectionExpression method inside search.js when updating the function below
3452var getRenditionPrintTypeQuery = function() {
3453    return "PrintType=(?\"." + getRenditionPrintTypeExtensions().join("\" OR ?\".") + "\")";
3454}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.