testing commit, please ignore
[kcls-web.git] / opac / common / js / combined.js
1 function $(id) { return getId(id); }
2 function getId(id) {
3         return document.getElementById(id);
4 }
5
6 function swapCSSClass(obj, old, newc ) {
7         removeCSSClass(obj, old );
8         addCSSClass(obj, newc );
9 }
10
11
12 function addCSSClass(e,c) {
13         if(!e || !c) return;
14
15         var css_class_string = e.className;
16         var css_class_array;
17
18         if(css_class_string)
19                 css_class_array = css_class_string.split(/\s+/);
20
21         var string_ip = ""; /*strip out nulls*/
22         for (var css_class in css_class_array) {
23                 if (css_class_array[css_class] == c) { return; }
24                 if(css_class_array[css_class] !=null)
25                         string_ip += css_class_array[css_class] + " ";
26         }
27         string_ip += c;
28         e.className = string_ip;
29 }
30
31 function removeCSSClass(e, c) {
32         if(!e || !c) return;
33
34         var css_class_string = '';
35
36         var css_class_array = e.className;
37         if( css_class_array )
38                 css_class_array = css_class_array.split(/\s+/);
39
40         var first = 1;
41         for (var css_class in css_class_array) {
42                 if (css_class_array[css_class] != c) {
43                         if (first == 1) {
44                                 css_class_string = css_class_array[css_class];
45                                 first = 0;
46                         } else {
47                                 css_class_string = css_class_string + ' ' +
48                                         css_class_array[css_class];
49                         }
50                 }
51         }
52         e.className = css_class_string;
53 }
54
55
56 /*returns the character code pressed that caused the event */
57 function grabCharCode(evt) {
58    evt = (evt) ? evt : ((window.event) ? event : null); 
59    if( evt ) {
60       return (evt.charCode ? evt.charCode : 
61          ((evt.which) ? evt.which : evt.keyCode ));
62    } else { return -1; }
63 }       
64
65
66 /* returns true if the user pressed enter */
67 function userPressedEnter(evt) {
68    var code = grabCharCode(evt);
69    if(code==13||code==3) return true;
70    return false;
71 }   
72
73
74 function goTo(url) {
75         /* setTimeout because ie sux */
76         setTimeout( function(){ location.href = url; }, 0 );
77 }
78
79
80 function removeChildren(dom) {
81         if(!dom) return;
82         while(dom.childNodes[0])
83                 dom.removeChild(dom.childNodes[0]);
84 }
85
86 function appendClear(node, child) {
87         if(typeof child =='string') child = text(child);
88         removeChildren(node);
89         node.appendChild(child);
90 }
91
92
93 function instanceOf(object, constructorFunction) {
94
95    if(!IE) {
96       while (object != null) {
97          if (object == constructorFunction.prototype)
98             return true;
99          object = object.__proto__;
100       }
101    } else {
102       while(object != null) {
103          if( object instanceof constructorFunction )
104             return true;
105          object = object.__proto__;
106       }
107    }
108    return false;
109 }         
110
111
112 /* ------------------------------------------------------------------------------------------- */
113 /* detect my browser */
114 var isMac, NS, NS4, NS6, IE, IE4, IEmac, IE4plus, IE5, IE5plus, IE6, IEMajor, ver4, Safari;
115 function detect_browser() {       
116
117    isMac = (navigator.appVersion.indexOf("Mac")!=-1) ? true : false;
118    NS = (navigator.appName == "Netscape") ? true : false;
119    NS4 = (document.layers) ? true : false;
120    IE = (navigator.appName == "Microsoft Internet Explorer") ? true : false;
121    IEmac = ((document.all)&&(isMac)) ? true : false;
122    IE4plus = (document.all) ? true : false;
123    IE4 = ((document.all)&&(navigator.appVersion.indexOf("MSIE 4.")!=-1)) ? true : false;
124    IE5 = ((document.all)&&(navigator.appVersion.indexOf("MSIE 5.")!=-1)) ? true : false;
125    IE6 = ((document.all)&&(navigator.appVersion.indexOf("MSIE 6.")!=-1)) ? true : false;
126    ver4 = (NS4 || IE4plus) ? true : false;
127    NS6 = (!document.layers) && (navigator.userAgent.indexOf('Netscape')!=-1)?true:false;
128    Safari = navigator.userAgent.match(/Safari/);
129
130    IE5plus = IE5 || IE6;
131    IEMajor = 0;
132
133    if (IE4plus) {
134       var start = navigator.appVersion.indexOf("MSIE");
135       var end = navigator.appVersion.indexOf(".",start);
136       IEMajor = parseInt(navigator.appVersion.substring(start+5,end));
137       IE5plus = (IEMajor>=5) ? true : false;
138    }
139 }  
140 detect_browser();
141 /* ------------------------------------------------------------------------------------------- */
142
143
144 function text(t) {
145         if(t == null) t = "";
146         return document.createTextNode(t);
147 }
148
149 function elem(name, attrs, txt) {
150     var e = document.createElement(name);
151     if (attrs) {
152         for (key in attrs) {
153                           if( key == 'id') e.id = attrs[key];
154                           else e.setAttribute(key, attrs[key]);
155         }
156     }
157     if (txt) e.appendChild(text(txt));
158     return e;
159 }                   
160
161
162 /* sel is the selector object, sets selected on the 
163         option with the given value. case does not matter...*/
164 function setSelector( sel, value ) {
165         if(sel && value != null) {
166                 for( var i = 0; i!= sel.options.length; i++ ) { 
167                         if( sel.options[i] ) {
168                                 var val = sel.options[i].value;
169                                 if( val == null || val == "" ) /* for IE */
170                                         val = sel.options[i].innerHTML;
171                                 value += ""; /* in case of number */ 
172                                 if( val && val.toLowerCase() == value.toLowerCase() ) {
173                                         sel.selectedIndex = i;
174                                         sel.options[i].selected = true;
175                                         return true;
176                                 }
177                         }
178                 }
179         }
180         return false;
181 }
182
183 function setSelectorRegex( sel, regex ) {
184         if(sel && regex != null) {
185                 for( var i = 0; i!= sel.options.length; i++ ) { 
186                         if( sel.options[i] ) {
187                                 var val = sel.options[i].value;
188                                 if( val == null || val == "" ) /* for IE */
189                                         val = sel.options[i].innerHTML;
190                                 value += ""; /* in case of number */ 
191                                 if( val && val.match(regex) ) {
192                                         sel.selectedIndex = i;
193                                         sel.options[i].selected = true;
194                                         return true;
195                                 }
196                         }
197                 }
198         }
199         return false;
200 }
201
202 function getSelectorVal( sel ) {
203         if(!sel) return null;
204         var idx = sel.selectedIndex;
205         if( idx < 0 ) return null;
206         var o = sel.options[idx];
207         var v = o.value; 
208         if(v == null) v = o.innerHTML;
209         return v;
210 }
211
212 function getSelectorName( sel ) {
213         var o = sel.options[sel.selectedIndex];
214         var v = o.name;
215         if(v == null || v == undefined || v == "") v = o.innerHTML;
216         return v;
217 }
218
219 function setSelectorByName( sel, name ) {
220         for( var o in sel.options ) {
221                 var opt = sel.options[o];
222                 if( opt.name == name || opt.innerHTML == name ) {
223                         sel.selectedIndex = o;
224                         opt.selected = true;
225                 }
226         }
227 }
228
229 function findSelectorOptByValue( sel, val ) {
230         for( var i = 0; i < sel.options.length; i++ ) {
231                 var opt = sel.options[i];
232                 if( opt.value == val ) return opt;
233         }
234         return null;
235 }
236
237 function debugSelector(sel) {
238         var s = 'Selector\n';
239         for( var i = 0; i != sel.options.length; i++ ) {
240                 var o = sel.options[i];
241                 s += "\t" + o.innerHTML + "\n";
242         }
243         return s;
244 }
245
246 function findParentByNodeName(node, name) {
247         while( ( node = node.parentNode) ) 
248                 if (node.nodeName == name) return node;
249         return null;
250 }
251
252 /* returns only elements in nodes childNodes list, not sub-children */
253 function getElementsByTagNameFlat( node, name ) {
254         var elements = [];
255         for( var e in node.childNodes ) {
256                 var n = node.childNodes[e];
257                 if( n && n.nodeName == name ) elements.push(n);
258         }
259         return elements;
260 }
261
262 /* expects a tree with a id() method on each node and a 
263 children() method to get to each node */
264 function findTreeItemById( tree, id ) {
265         if( tree.id() == id ) return tree;
266         for( var c in tree.children() ) {
267                 var found = findTreeItemById( tree.children()[c], id );
268                 if(found) return found;
269         }
270         return null;
271 }
272
273 /* returns null if none of the tests are true.  returns sub-array of 
274 matching array items otherwise */
275 function grep( arr, func ) {
276         var results = [];
277         if(!arr) return null;
278         if( arr.constructor == Array ) {
279                 for( var i = 0; i < arr.length; i++ ) {
280                         if( func(arr[i]) ) 
281                                 results.push(arr[i]);
282                 }
283         } else {
284                 for( var i in arr ) {
285                         if( func(arr[i]) ) 
286                                 results.push(arr[i]);
287                 }
288         }
289         if(results.length > 0) return results;
290         return null;
291 }
292
293 function ogrep( obj, func ) {
294         var results = {};
295         var found = false;
296         for( var i in obj ) {
297                 if( func(obj[i]) ) {
298                         results[i] = obj[i];
299                         found = true;
300                 }
301         }
302         if(found) return results;
303         return null;
304 }
305
306 function doSelectorActions(sel) {
307         if((IE || Safari) && sel) { 
308                 sel.onchange = function() {
309                         var o = sel.options[sel.selectedIndex];
310                         if(o && o.onclick) o.onclick()
311                 }
312         }
313 }
314
315 /* if index < 0, the item is pushed onto the end */
316 function insertSelectorVal( selector, index, name, value, action, indent ) {
317         if( index < 0 ) index = selector.options.length;
318         var a = [];
319         for( var i = selector.options.length; i != index; i-- ) 
320                 a[i] = selector.options[i-1];
321
322         var opt = setSelectorVal( selector, index, name, value, action, indent );
323
324         for( var i = index + 1; i < a.length; i++ ) 
325                 selector.options[i] = a[i];
326
327         return opt;
328 }
329
330 /* changes the value of the option at the specified index */
331 function setSelectorVal( selector, index, name, value, action, indent ) {
332         if(!indent || indent < 0) indent = 0;
333         indent = parseInt(indent);
334
335         var option;
336
337         if(IE) {
338                 var pre = elem("pre");
339                 for( var i = 0; i != indent; i++ )
340                         pre.appendChild(text("   "));
341
342                 pre.appendChild(text(name));
343                 option = new Option("", value);
344                 selector.options[index] = option;
345                 option.appendChild(pre);
346         
347         } else {
348                 indent = indent * 14;
349                 option= new Option(name, value);
350                 option.setAttribute("style", "padding-left: "+indent+'px;');
351                 selector.options[index] = option;
352                 if(action) option.onclick = action;
353         }
354
355         if(action) option.onclick = action;
356         return option;
357 }
358
359
360 /* split on spaces.  capitalize the first /\w/ character in
361    each substring */
362 function normalize(val) {
363         return val; /* disable me for now */
364
365    if(!val) return ""; 
366
367    var newVal = '';
368    try {val = val.split(' ');} catch(E) {return val;}
369    var reg = /\w/;
370
371    for( var c = 0; c < val.length; c++) {
372
373       var string = val[c];
374       var cap = false; 
375       for(var x = 0; x != string.length; x++) {
376
377          if(!cap) {
378             var ch = string.charAt(x);
379             if(reg.exec(ch + "")) {
380                newVal += string.charAt(x).toUpperCase();
381                cap = true;
382                continue;
383             }
384          }
385
386          newVal += string.charAt(x).toLowerCase();
387       }
388       if(c < (val.length-1)) newVal += " ";
389    }
390
391    newVal = newVal.replace(/\s*\.\s*$/,'');
392    newVal = newVal.replace(/\s*\/\s*\/\s*$/,' / ');
393    newVal = newVal.replace(/\s*\/\s*$/,'');
394
395    return newVal;
396 }
397
398
399 /* returns true if n is null or stringifies to 'undefined' */
400 function isNull(n) {
401         if( n == null || n == undefined || n.toString().toLowerCase() == "undefined" 
402                 || n.toString().toLowerCase() == "null" )
403                 return true;
404         return false;
405 }
406
407
408 /* find nodes with an attribute of 'name' that equals nodeName */
409
410 function $n( root, nodeName ) { return findNodeByName(root,nodeName); }
411
412 function findNodeByName(root, nodeName) {
413         if( !root || !nodeName) return null;
414
415         if(root.nodeType != 1) return null;
416
417         if(root.getAttribute("name") == nodeName || root.name == nodeName ) 
418                 return root;
419
420         var children = root.childNodes;
421
422         for( var i = 0; i != children.length; i++ ) {
423                 var n = findNodeByName(children[i], nodeName);
424                 if(n) return n;
425         }
426
427         return null;
428 }
429
430
431 /* truncates the string at 'size' characters and appends a '...' to the end */
432 function truncate(string, size) {
433         if(string && size != null && 
434                         size > -1 && string.length > size) 
435                 return string.substr(0, size) + "... "; 
436         return string;
437 }
438
439
440 /* style sheets must have a 'name' attribute for these functions to work */
441 function setActivateStyleSheet(name) {
442         var i, a, main;
443         for (i = 0; (a = document.getElementsByTagName ("link")[i]); i++) {
444                 if (a.getAttribute ("rel").indexOf ("style") != -1 && a.getAttribute ("name")) {
445                         a.disabled = true;
446                         if (a.getAttribute ("name").indexOf(name) != -1)
447                                 a.disabled = false;
448                 }
449         }
450 }
451
452
453 /* ----------------------------------------------------- */
454 var currentFontSize;
455 function scaleFonts(type) {
456
457         var size                = "";
458         var ssize       = "";
459         var size2       = "";
460         var a;
461         
462         if(!currentFontSize) currentFontSize = 'regular';
463         if(currentFontSize == 'regular' && type == 'regular' ) return;
464         if( currentFontSize == type ) return;
465         currentFontSize = type;
466
467         switch(type) {
468                 case "large":  /* these are arbitrary.. but they seem to work ok in FF/IE */
469                         size = "142%"; 
470                         size2 = "107%"; 
471                         ssize = "94%";
472                         break;
473         }
474
475         document.getElementsByTagName('body')[0].style.fontSize = size;
476         for (i = 0; (a = document.getElementsByTagName ("td")[i]); i++) a.style.fontSize = size;;
477         for (i = 0; (a = document.getElementsByTagName ("div")[i]); i++) a.style.fontSize = ssize;
478         for (i = 0; (a = document.getElementsByTagName ("option")[i]); i++) a.style.fontSize = ssize;
479         for (i = 0; (a = document.getElementsByTagName ("li")[i]); i++) a.style.fontSize = ssize;
480         for (i = 0; (a = document.getElementsByTagName ("span")[i]); i++) a.style.fontSize = ssize;
481         for (i = 0; (a = document.getElementsByTagName ("select")[i]); i++) a.style.fontSize = ssize;
482         for (i = 0; (a = document.getElementsByTagName ("a")[i]); i++) a.style.fontSize = size2;
483 }
484
485
486 function sortWordsIgnoreCase(a, b) {
487         a = a.toLowerCase();
488         b = b.toLowerCase();
489         if(a>b) return 1;
490         if(a<b) return -1;
491         return 0;
492 }
493
494
495 function getSelectedList(sel) {
496         if(!sel) return [];
497         var vals = [];
498         for( var i = 0; i != sel.options.length; i++ ) {
499                 if(sel.options[i].selected)
500                         vals.push(sel.options[i].value);
501         }
502         return vals;
503 }
504
505
506 function setEnterFunc(node, func) {
507         if(!(node && func)) return;
508         node.onkeydown = function(evt) {
509                 if( userPressedEnter(evt)) func();
510         }
511 }
512
513 function iterate( arr, callback ) {
514         for( var i = 0; arr && i < arr.length; i++ ) 
515                 callback(arr[i]);
516 }
517
518
519
520
521 /* taken directly from the JSAN util.date library */
522 /* but changed from the util.date.interval_to_seconds invocation, 
523 because JSAN will assume the whole library is already loaded if 
524 it sees that, and the staff client uses both this file and the
525 JSAN library*/
526 function interval_to_seconds( $interval ) {
527
528         $interval = $interval.replace( /and/, ',' );
529         $interval = $interval.replace( /,/, ' ' );
530         
531         var $amount = 0;
532         var results = $interval.match( /\s*\+?\s*(\d+)\s*(\w{1})\w*\s*/g);  
533         for( var i = 0; i < results.length; i++ ) {
534                 if(!results[i]) continue;
535                 var result = results[i].match( /\s*\+?\s*(\d+)\s*(\w{1})\w*\s*/ );
536                 if (result[2] == 's') $amount += result[1] ;
537                 if (result[2] == 'm') $amount += 60 * result[1] ;
538                 if (result[2] == 'h') $amount += 60 * 60 * result[1] ;
539                 if (result[2] == 'd') $amount += 60 * 60 * 24 * result[1] ;
540                 if (result[2] == 'w') $amount += 60 * 60 * 24 * 7 * result[1] ;
541                 if (result[2] == 'M') $amount += ((60 * 60 * 24 * 365)/12) * result[1] ;
542                 if (result[2] == 'y') $amount += 60 * 60 * 24 * 365 * result[1] ;
543         }
544         return $amount;
545 }
546
547
548 function openWindow( data ) {
549         if( isXUL() ) {
550                 var data = window.escape(
551                         '<html><head><title></title></head><body>' + data + '</body></html>');
552
553                 xulG.window_open(
554                         'data:text/html,' + data,
555                         '', 
556                         'chrome,resizable,width=700,height=500'); 
557
558         } else {
559                 win = window.open('','', 'resizable,width=700,height=500,scrollbars=1'); 
560                 win.document.body.innerHTML = data;
561         }
562 }
563
564
565 /* alerts the innerhtml of the node with the given id */
566 function alertId(id) {
567         var node = $(id);
568         if(node) alert(node.innerHTML);
569 }
570
571 function alertIdText(id, text) {
572         var node = $(id);
573    if(!node) return;
574    if(text)
575       alert(text + '\n\n' + node.innerHTML);
576    else 
577            alert(node.innerHTML);
578 }
579
580 function confirmId(id) {
581         var node = $(id);
582         if(node) return confirm(node.innerHTML);
583 }
584
585
586 function goBack() { history.back(); }
587 function goForward() { history.forward(); }
588
589
590 function uniquify(arr) {
591         if(!arr) return [];
592         var newarr = [];
593         for( var i = 0; i < arr.length; i++ ) {
594                 var item = arr[i];
595                 if( ! grep( newarr, function(x) {return (x == item);}))
596                         newarr.push(item);
597         }
598         return newarr;
599 }
600
601 function contains(arr, item) {
602         for( var i = 0; i < arr.length; i++ ) 
603                 if( arr[i] == item ) return true;
604         return false;
605 }
606
607 function isTrue(i) {
608         return (i && !(i+'').match(/f/i) );
609 }
610
611
612 /* builds a JS date object with the given info.  The given data
613         has to be valid (e.g. months == 30 is not valid).  Returns NULL on 
614         invalid date 
615         Months are 1-12 (unlike the JS date object)
616         */
617
618 function buildDate( year, month, day, hours, minutes, seconds ) {
619
620         if(!year) year = 0;
621         if(!month) month = 1;
622         if(!day) day = 1;
623         if(!hours) hours = 0;
624         if(!minutes) minutes = 0;
625         if(!seconds) seconds = 0;
626
627         var d = new Date(year, month - 1, day, hours, minutes, seconds);
628         
629         _debug('created date with ' +
630                 (d.getYear() + 1900) +'-'+
631                 (d.getMonth() + 1) +'-'+
632                 d.getDate()+' '+
633                 d.getHours()+':'+
634                 d.getMinutes()+':'+
635                 d.getSeconds());
636
637
638         if( 
639                 (d.getYear() + 1900) == year &&
640                 d.getMonth()    == (month - 1) &&
641                 d.getDate()             == new Number(day) &&
642                 d.getHours()    == new Number(hours) &&
643                 d.getMinutes() == new Number(minutes) &&
644                 d.getSeconds() == new Number(seconds) ) {
645                 return d;
646         }
647
648         return null;
649 }
650
651 function mkYearMonDay(date) {
652         if(!date) date = new Date();
653         var y = date.getYear() + 1900;
654         var m = (date.getMonth() + 1)+'';
655         var d = date.getDate()+'';
656         if(m.length == 1) m = '0'+m;
657         if(d.length == 1) d = '0'+d;
658         return y+'-'+m+'-'+d;
659 }
660
661
662 function debugFMObject(obj) {
663         if(typeof obj != 'object' ) return obj;
664         _debug("---------------------");
665         var keys = fmclasses[obj.classname];
666         if(!keys) { _debug(formatJSON(js2JSON(obj))); return; }
667
668         keys.sort();
669         for( var i = 0; i < keys.length; i++ ) {
670                 var key = keys[i];
671                 while( key.length < 12 ) key += ' ';
672                 var val = obj[keys[i]]();
673                 if( typeof val == 'object' ) {
674                         _debug(key+' :=\n');
675                         _debugFMObject(val);
676                 } else {
677                         _debug(key+' = ' +val);
678                 }
679
680         }
681         _debug("---------------------");
682 }
683
684
685 function getTableRows(tbody) {
686     var rows = [];
687     if(!tbody) return rows;
688
689     var children = tbody.childNodes;
690     if(!children) return rows;
691
692     for(var i = 0; i < children.length; i++) {
693         var child = children[i];
694         if(child.nodeName.match(/^tr$/i)) 
695             rows.push(child);
696     }
697     return rows;
698 }
699
700 function getObjectKeys(obj) {
701     keys = []
702     for(var k in obj)
703         keys.push(k)
704     return keys;
705 }
706 /* Export some constants  ----------------------------------------------------- */
707
708 var SHOW_MR_DEFAULT = false; /* true if we show metarecords by default */
709
710 //var DO_AUTHORITY_LOOKUPS = false;
711 var DO_AUTHORITY_LOOKUPS = true;
712
713 var STAFF_WEB_BASE_PATH = '/eg'; // root of the web-based staff interfaces
714
715 /* URL param names */
716 var PARAM_TERM                  = "t";                  /* search term */
717 var PARAM_FACET                 = "ft";                 /* facet term */
718 var PARAM_STYPE         = "tp";                 /* search type */
719 var PARAM_LOCATION      = "l";                  /* current location */
720 var PARAM_LASSO = "sg";                 /* current location */
721 var PARAM_DEPTH         = "d";                  /* search depth */
722 var PARAM_FORM                  = "f";                  /* search format */
723 var PARAM_OFFSET                = "o";                  /* search offset */
724 var PARAM_COUNT         = "c";                  /* hits per page */
725 var PARAM_HITCOUNT      = "hc";                 /* hits per page */
726 var PARAM_MRID                  = "m";                  /* metarecord id */
727 var PARAM_RID                   = "r";                  /* record id */
728 var PARAM_RLIST         = "rl";
729 var PARAM_ORIGLOC               = "ol";                 /* the original location */
730 var PARAM_AUTHTIME      = "at";                 /* inactivity timeout in seconds */
731 var PARAM_ADVTERM               = "adv";                        /* advanced search term */
732 var PARAM_ADVTYPE               = "adt";                        /* the advanced search type */
733 var PARAM_RTYPE         = "rt";
734 var PARAM_SORT                  = "s";
735 var PARAM_SORT_DIR      = "sd";
736 var PARAM_DEBUG         = "dbg";
737 var PARAM_CN                    = "cn";
738 var PARAM_LITFORM               = 'lf';
739 var PARAM_ITEMFORM      = 'if';
740 var PARAM_ITEMTYPE      = 'it';
741 var PARAM_BIBLEVEL      = 'bl';
742 var PARAM_AUDIENCE      = 'a';
743 var PARAM_SEARCHES      = 'ss';
744 var PARAM_LANGUAGE      = 'la';
745 var PARAM_TFORM         = 'tf'; /* temporary format for title result pages */
746 var PARAM_RDEPTH                = 'rd';
747 var PARAM_REDIR         = 're'; /* true if we have been redirected by IP (we're at a real lib) */
748 var PARAM_AVAIL     = 'av'; /* limit search results to available items */
749 var PARAM_COPYLOCS  = 'cl'; // copy (shelving) locations
750 var PARAM_PUBD_BEFORE = 'pdb';
751 var PARAM_PUBD_AFTER = 'pda';
752 var PARAM_PUBD_BETWEEN = 'pdt';
753 var PARAM_PUBD_DURING = 'pdd';
754 var PARAM_NOPERSIST_SEARCH = 'nps';
755
756 /* URL param values (see comments above) */
757 var TERM;  
758 var FACET;  
759 var STYPE;  
760 var LOCATION;  
761 var LASSO;  
762 var DEPTH;  
763 var FORM; 
764 var OFFSET;
765 var COUNT;  
766 var HITCOUNT;  
767 var RANKS; 
768 var FONTSIZE;
769 var ORIGLOC;
770 var AUTHTIME;
771 var ADVTERM;
772 var ADVTYPE;
773 var MRID;
774 var RID;
775 var RTYPE;
776 var SORT;
777 var SORT_DIR;
778 var RLIST;
779 var DEBUG;
780 var CALLNUM;
781 var LITFORM;
782 var ITEMFORM;
783 var ITEMTYPE;
784 var BIBLEVEL;
785 var AUDIENCE;
786 var SEARCHES;
787 var LANGUAGE;
788 var TFORM;
789 var RDEPTH;
790 var AVAIL;
791 var COPYLOCS;
792 var PUBD_BEFORE;
793 var PUBD_AFTER;
794 var PUBD_BETWEEN;
795 var PUBD_DURING;
796
797 /* cookie values */
798 var SBEXTRAS; 
799 var SKIN;
800
801 /* cookies */
802 var COOKIE_SB           = "sbe";
803 var COOKIE_SES          = "ses";
804 //var COOKIE_IDS                = "ids"; /* list of mrecord ids */
805 //var COOKIE_SRIDS      = "srids"; /* record ids cached from a search */
806 var COOKIE_FONT = "fnt";
807 var COOKIE_SKIN = "skin";
808 var COOKIE_RIDS = "rids"; /* list of record ids */
809 var COOKIE_SEARCH = 'sr';
810
811 /* pages */
812 var MRESULT             = "mresult";
813 var RRESULT             = "rresult";
814 var RDETAIL             = "rdetail";
815 var MYOPAC              = "myopac";
816 var ADVANCED    = "advanced";
817 var HOME                        = "home";
818 var BBAGS               = "bbags";
819 var REQITEMS    = "reqitems";
820 var CNBROWSE    = "cnbrowse";
821
822 /* search type (STYPE) options */
823 var STYPE_AUTHOR        = "author";
824 var STYPE_TITLE = "title";
825 var STYPE_SUBJECT       = "subject";
826 var STYPE_SERIES        = "series";
827 var STYPE_KEYWORD       = "keyword";
828
829 /* record-level search types */
830 var RTYPE_MRID          = "mrid";
831 var RTYPE_COOKIE        = "cookie";
832 var RTYPE_AUTHOR        = STYPE_AUTHOR;
833 var RTYPE_SUBJECT       = STYPE_SUBJECT;
834 var RTYPE_TITLE = STYPE_TITLE;
835 var RTYPE_SERIES        = STYPE_SERIES;
836 var RTYPE_KEYWORD       = STYPE_KEYWORD;
837 var RTYPE_LIST          = "list";
838 var RTYPE_MULTI = 'multi';
839 var RTYPE_MARC          = 'marc';
840 var RTYPE_ISBN          = 'isbn';
841 var RTYPE_ISSN          = 'issn';
842 var RTYPE_TCN           = 'tcn';
843
844 var SORT_TYPE_REL                       = "rel";
845 var SORT_TYPE_AUTHOR            = STYPE_AUTHOR; 
846 var SORT_TYPE_TITLE             = STYPE_TITLE;
847 var SORT_TYPE_PUBDATE   = "pubdate";
848 var SORT_DIR_ASC                        = "asc";
849 var SORT_DIR_DESC                       = "desc";
850
851 /* types of advanced search */
852 var ADVTYPE_MULTI = 'ml';
853 var ADVTYPE_MARC        = 'ma';
854
855 /*
856 var ADVTYPE_ISBN        = 'isbn';
857 var ADVTYPE_ISSN        = 'issn';
858 */
859
860 var LOGOUT_WARNING_TIME = 30; /* "head up" for session timeout */
861
862 /* user preferences */
863 var PREF_HITS_PER               = 'opac.hits_per_page';
864 var PREF_DEF_FONT               = 'opac.default_font';
865 var PREF_HOLD_NOTIFY = 'opac.hold_notify';
866 var PREF_DEF_LOCATION = 'opac.default_search_location';
867 var PREF_DEF_DEPTH      = 'opac.default_search_depth';
868
869
870 /** If enabled, added content attribution links will be 
871     made visible where appropriate.  The added content vendor name 
872     and URL are defined in the entities in opac.dtd
873     */
874 var ENABLE_ADDED_CONTENT_ATTRIB_LINKS = false;
875
876
877 /* container for global variables shared accross pages */
878 var G           = {};
879 G.user  = null; /* global user object */
880 G.ui            = {} /* cache of UI components */
881
882
883 /* regexes */
884 var REGEX_BARCODE = /^\d+/; /* starts with a number */
885 var REGEX_PHONE = /^\d{3}-\d{3}-\d{4}$/; /* 111-222-3333 */
886
887
888 /* call me after page init and I will load references 
889         to all of the ui object id's defined below 
890         They will be stored in G.ui.<page>.<thingy>
891  */
892 function loadUIObjects() {
893         for( var p in config.ids ) {
894                 G.ui[p] = {};
895                 for( var o in config.ids[p] ) 
896                         G.ui[p][o] = getId(config.ids[p][o]);
897         }
898 }
899
900 /* try our best to free memory */
901 function clearUIObjects() {
902         for( var p in config.ids ) {
903                 for( var o in config.ids[p] ) {
904                         if(G.ui[p][o]) {
905                                 G.ui[p][o].onclick = null;
906                                 G.ui[p][o].onkeydown = null;
907                                 G.ui[p][o] = null;
908                         }
909                 }
910                 G.ui[p] = null;
911         }
912 }
913
914 /* ---------------------------------------------------------------------------- 
915         Set up ID's and CSS classes 
916         Any new ids, css, etc. may be added by giving the unique names and putting 
917         them into the correct scope 
918 /* ---------------------------------------------------------------------------- */
919
920 var config = {};
921
922 /* Set up the page names */
923 config.page = {};
924 config.page[HOME]                       = "index.xml";
925 config.page[ADVANCED]   = "advanced.xml";
926 config.page[MRESULT]            = "mresult.xml";
927 config.page[RRESULT]            = "rresult.xml";
928 config.page[MYOPAC]             = "myopac.xml";
929 config.page[RDETAIL]            = "rdetail.xml";
930 config.page[BBAGS]              = "bbags.xml";
931 config.page[REQITEMS]   = "reqitems.xml";
932 config.page[CNBROWSE]   = "cnbrowse.xml";
933
934 /* themes */
935 config.themes = {};
936
937 /* set up images  */
938 config.images = {};
939 config.images.logo = "main_logo.jpg";
940
941
942 /* set up ID's, CSS, and node names */
943 config.ids                              = {};
944 config.ids.result               = {};
945 config.ids.mresult      = {};
946 config.ids.advanced     = {};
947 config.ids.rresult      = {};
948 config.ids.myopac               = {};
949 config.ids.rdetail      = {};
950
951 config.css                              = {};
952 config.css.result               = {};
953 config.css.mresult      = {};
954 config.css.advanced     = {};
955 config.css.rresult      = {};
956 config.css.myopac               = {};
957 config.css.rdetail      = {};
958
959 config.names                    = {};
960 config.names.result     = {};
961 config.names.mresult = {};
962 config.names.advanced = {};
963 config.names.rresult = {};
964 config.names.myopac     = {};
965 config.names.rdetail = {};
966
967
968 /* id's shared accross skins. These *must* be defined */
969 config.ids.common = {};
970 config.ids.common.loading                       = "loading_div";                
971 config.ids.common.canvas                        = "canvas";                             
972 config.ids.common.canvas_main           = "canvas_main";                
973 config.ids.common.org_tree                      = "org_tree";                   
974 config.ids.common.org_container = "org_container";
975
976 config.ids.xul = {};
977
978
979 /* shared CSS */
980 config.css.hide_me = "hide_me";
981 config.css.dim = "dim";
982 config.css.dim2 = "dim2";
983
984
985 /* ---------------------------------------------------------------------------- */
986 /* These are pages that may replace the canvas */
987 /* ---------------------------------------------------------------------------- */
988 config.ids.altcanvas = {};
989
990
991
992 /* ---------------------------------------------------------------------------- */
993 /* Methods are defined as service:method 
994         An optional 3rd component is when a method is followed by a :1, such methods
995         have a staff counterpart and should have ".staff" appended to the method 
996         before the method is called when in XUL mode */
997
998 var SEARCH_MRS                                          = 'open-ils.search:open-ils.search.metabib.multiclass:1';
999 var SEARCH_RS                                           = 'open-ils.search:open-ils.search.biblio.multiclass:1';
1000 var SEARCH_MRS_QUERY                    = 'open-ils.search:open-ils.search.metabib.multiclass.query:1';
1001 var SEARCH_RS_QUERY             = 'open-ils.search:open-ils.search.biblio.multiclass.query:1';
1002 var FETCH_SEARCH_RIDS                   = "open-ils.search:open-ils.search.biblio.record.class.search:1";
1003 var CREATE_MFHD_RECORD                  = "open-ils.cat:open-ils.cat.serial.record.xml.create";
1004 var DELETE_MFHD_RECORD                  = "open-ils.cat:open-ils.cat.serial.record.delete";
1005 var FETCH_MFHD_SUMMARY                  = "open-ils.search:open-ils.search.serial.record.bib.retrieve";
1006 var FETCH_MRMODS                                        = "open-ils.search:open-ils.search.biblio.metarecord.mods_slim.retrieve";
1007 var FETCH_MODS_FROM_COPY                = "open-ils.search:open-ils.search.biblio.mods_from_copy";
1008 var FETCH_MR_COPY_COUNTS                = "open-ils.search:open-ils.search.biblio.metarecord.copy_count:1";
1009 var FETCH_RIDS                                          = "open-ils.search:open-ils.search.biblio.metarecord_to_records:1";
1010 var FETCH_RMODS                                 = "open-ils.search:open-ils.search.biblio.record.mods_slim.retrieve";
1011 var FETCH_R_COPY_COUNTS                 = "open-ils.search:open-ils.search.biblio.record.copy_count:1";
1012 var FETCH_FLESHED_USER                  = "open-ils.actor:open-ils.actor.user.fleshed.retrieve";
1013 var FETCH_SESSION                                       = "open-ils.auth:open-ils.auth.session.retrieve";
1014 var LOGIN_INIT                                          = "open-ils.auth:open-ils.auth.authenticate.init";
1015 var LOGIN_COMPLETE                              = "open-ils.auth:open-ils.auth.authenticate.complete";
1016 var LOGIN_DELETE                                        = "open-ils.auth:open-ils.auth.session.delete";
1017 var FETCH_USER_PREFS                            = "open-ils.actor:open-ils.actor.patron.settings.retrieve"; 
1018 var UPDATE_USER_PREFS                   = "open-ils.actor:open-ils.actor.patron.settings.update"; 
1019 var FETCH_COPY_STATUSES                 = "open-ils.search:open-ils.search.config.copy_status.retrieve.all";
1020 var FETCH_COPY_LOCATION_COUNTS_SUMMARY  = "open-ils.search:open-ils.search.biblio.copy_location_counts.summary.retrieve";
1021 var FETCH_COPY_COUNTS_SUMMARY   = "open-ils.search:open-ils.search.biblio.copy_counts.summary.retrieve";
1022 //var FETCH_COPY_COUNTS_SUMMARY = "open-ils.search:open-ils.search.biblio.copy_counts.location.summary.retrieve";
1023 var FETCH_MARC_HTML                             = "open-ils.search:open-ils.search.biblio.record.html";
1024 var FETCH_CHECKED_OUT_SUM               = "open-ils.actor:open-ils.actor.user.checked_out";
1025 var FETCH_HOLDS                                 = "open-ils.circ:open-ils.circ.holds.retrieve";
1026 var FETCH_FINES_SUMMARY                 = "open-ils.actor:open-ils.actor.user.fines.summary";
1027 var FETCH_TRANSACTIONS                  = "open-ils.actor:open-ils.actor.user.transactions.have_charge.fleshed";
1028 var FETCH_MONEY_BILLING                 = 'open-ils.circ:open-ils.circ.money.billing.retrieve.all';
1029 var FETCH_CROSSREF                              = "open-ils.search:open-ils.search.authority.crossref";
1030 var FETCH_CROSSREF_BATCH                = "open-ils.search:open-ils.search.authority.crossref.batch";
1031 var CREATE_HOLD                                 = "open-ils.circ:open-ils.circ.holds.create";
1032 var CREATE_HOLD_OVERRIDE                = "open-ils.circ:open-ils.circ.holds.create.override";
1033 var CANCEL_HOLD                                 = "open-ils.circ:open-ils.circ.hold.cancel";
1034 var UPDATE_USERNAME                             = "open-ils.actor:open-ils.actor.user.username.update";
1035 var UPDATE_PASSWORD                             = "open-ils.actor:open-ils.actor.user.password.update";
1036 var UPDATE_EMAIL                                        = "open-ils.actor:open-ils.actor.user.email.update";
1037 var RENEW_CIRC                                          = "open-ils.circ:open-ils.circ.renew";
1038 var CHECK_SPELL                                 = "open-ils.search:open-ils.search.spellcheck";
1039 var FETCH_REVIEWS                                       = "open-ils.search:open-ils.search.added_content.review.retrieve.all";
1040 var FETCH_TOC                                           = "open-ils.search:open-ils.search.added_content.toc.retrieve";
1041 var FETCH_ACONT_SUMMARY                 = "open-ils.search:open-ils.search.added_content.summary.retrieve";
1042 var FETCH_USER_BYBARCODE                = "open-ils.actor:open-ils.actor.user.fleshed.retrieve_by_barcode";
1043 var FETCH_ADV_MARC_MRIDS                = "open-ils.search:open-ils.search.biblio.marc:1";
1044 var FETCH_ADV_ISBN_RIDS                 = "open-ils.search:open-ils.search.biblio.isbn";
1045 var FETCH_ADV_ISSN_RIDS                 = "open-ils.search:open-ils.search.biblio.issn";
1046 var FETCH_ADV_TCN_RIDS                  = "open-ils.search:open-ils.search.biblio.tcn";
1047 var FETCH_CNBROWSE                              = 'open-ils.search:open-ils.search.callnumber.browse';
1048 var FETCH_CONTAINERS                            = 'open-ils.actor:open-ils.actor.container.retrieve_by_class';
1049 var FETCH_CONTAINERS                            = 'open-ils.actor:open-ils.actor.container.retrieve_by_class';
1050 var CREATE_CONTAINER                            = 'open-ils.actor:open-ils.actor.container.create';
1051 var DELETE_CONTAINER                            = 'open-ils.actor:open-ils.actor.container.full_delete';
1052 var CREATE_CONTAINER_ITEM               = 'open-ils.actor:open-ils.actor.container.item.create';
1053 var DELETE_CONTAINER_ITEM               = 'open-ils.actor:open-ils.actor.container.item.delete';
1054 var FLESH_CONTAINER                             = 'open-ils.actor:open-ils.actor.container.flesh';
1055 var FLESH_PUBLIC_CONTAINER              = 'open-ils.actor:open-ils.actor.container.public.flesh';
1056 var UPDATE_CONTAINER                            = 'open-ils.actor:open-ils.actor.container.update';
1057 var FETCH_COPY                                          = 'open-ils.search:open-ils.search.asset.copy.retrieve';
1058 var FETCH_FLESHED_COPY                  = 'open-ils.search:open-ils.search.asset.copy.fleshed2.retrieve';
1059 var CHECK_HOLD_POSSIBLE                 = 'open-ils.circ:open-ils.circ.title_hold.is_possible';
1060 var UPDATE_HOLD                                 = 'open-ils.circ:open-ils.circ.hold.update';
1061 var FETCH_COPIES_FROM_VOLUME    = 'open-ils.search:open-ils.search.asset.copy.retrieve_by_cn_label:1';
1062 var FETCH_VOLUME_BY_INFO                = 'open-ils.search:open-ils.search.call_number.retrieve_by_info'; /* XXX staff method? */
1063 var FETCH_VOLUME                                        = 'open-ils.search:open-ils.search.asset.call_number.retrieve';
1064 var FETCH_ISSUANCE                                      = 'open-ils.serial:open-ils.serial.issuance.pub_fleshed.batch.retrieve';
1065 var FETCH_COPY_LOCATIONS                = 'open-ils.circ:open-ils.circ.copy_location.retrieve.all';
1066 var FETCH_COPY_NOTES                            = 'open-ils.circ:open-ils.circ.copy_note.retrieve.all';
1067 var FETCH_COPY_STAT_CATS                = 'open-ils.circ:open-ils.circ.asset.stat_cat_entries.fleshed.retrieve_by_copy';
1068 var FETCH_LIT_FORMS                             = 'open-ils.search:open-ils.search.biblio.lit_form_map.retrieve.all';
1069 var FETCH_ITEM_FORMS                            = 'open-ils.search:open-ils.search.biblio.item_form_map.retrieve.all';
1070 var FETCH_ITEM_TYPES                            = 'open-ils.search:open-ils.search.biblio.item_type_map.retrieve.all';
1071 var FETCH_BIB_LEVELS                            = 'open-ils.search:open-ils.search.biblio.bib_level_map.retrieve.all';
1072 var FETCH_AUDIENCES                             = 'open-ils.search:open-ils.search.biblio.audience_map.retrieve.all';
1073 //var FETCH_HOLD_STATUS                 = 'open-ils.circ:open-ils.circ.hold.status.retrieve';
1074 var FETCH_HOLD_STATUS                   = 'open-ils.circ:open-ils.circ.hold.queue_stats.retrieve';
1075 var FETCH_NON_CAT_CIRCS                 = 'open-ils.circ:open-ils.circ.open_non_cataloged_circulation.user';
1076 var FETCH_NON_CAT_CIRC                  = 'open-ils.circ:open-ils.circ.non_cataloged_circulation.retrieve';
1077 var FETCH_NON_CAT_TYPES                 = "open-ils.circ:open-ils.circ.non_cat_types.retrieve.all";
1078 var FETCH_BRE                                           = 'open-ils.search:open-ils.search.biblio.record_entry.slim.retrieve';
1079 var CHECK_USERNAME                              = 'open-ils.actor:open-ils.actor.username.exists';
1080 var FETCH_CIRC_BY_ID                            = 'open-ils.circ:open-ils.circ.retrieve';
1081 var FETCH_MR_DESCRIPTORS                = 'open-ils.search:open-ils.search.metabib.record_to_descriptors';
1082 var FETCH_HIGHEST_PERM_ORG              = 'open-ils.actor:open-ils.actor.user.perm.highest_org.batch';
1083 var FETCH_USER_NOTES                            = 'open-ils.actor:open-ils.actor.note.retrieve.all';
1084 var FETCH_ORG_BY_SHORTNAME              = 'open-ils.actor:open-ils.actor.org_unit.retrieve_by_shortname';
1085 var FETCH_BIB_ID_BY_BARCODE = 'open-ils.search:open-ils.search.bib_id.by_barcode';
1086 var FETCH_ORG_SETTING = 'open-ils.actor:open-ils.actor.ou_setting.ancestor_default';
1087
1088 /* ---------------------------------------------------------------------------- */
1089
1090
1091 /* ---------------------------------------------------------------------------- */
1092 /* event callback functions. Other functions may be appended to these vars to
1093         for added functionality.  */
1094
1095 G.evt                           = {}; /* events container */
1096
1097 function runEvt(scope, name, a, b, c, d, e, f, g) {
1098         var evt = G.evt[scope][name];
1099         for( var i in evt ) {
1100                 evt[i](a, b, c, d, e, f, g);    
1101         }
1102 }
1103
1104 /* creates a new event if it doesn't already exist */
1105 function createEvt(scope, name) {
1106         if(!G.evt[scope]) G.evt[scope] = {};
1107         if(G.evt[scope][name] == null)
1108                 G.evt[scope][name] = []; 
1109 }
1110
1111 function attachEvt(scope, name, action) {
1112         createEvt(scope, name);
1113         G.evt[scope][name].push(action);
1114 }
1115
1116 function detachAllEvt(scope, name) {
1117         G.evt[scope][name] = [];
1118 }
1119
1120
1121 createEvt("common", "init");                                            /* f() : what happens on page init */
1122 createEvt("common", "pageRendered");                    /* f() : what happens when the page is done (up to the skin to call this even)*/
1123 createEvt("common", "unload");                                  /* f() : what happens on window unload (clean memory, etc.)*/
1124 createEvt("common", "locationChanged");         /* f() : what happens when the location has changed */
1125 createEvt("common", "locationUpdated");         /* f() : what happens when the location has updated by the code */
1126
1127 createEvt("common", "run");                                             /* f() : make the page do stuff */
1128 createEvt("result", "idsReceived");                             /* f(ids) */
1129 createEvt("rresult", "recordDrawn");                    /* f(recordid, linkDOMNode) : after record is drawn, allow others (xul) to plugin actions */
1130 createEvt("result", "preCollectRecords");               /* f() we're about to go and grab the recs */
1131
1132 createEvt("result", "hitCountReceived");                /* f() : display hit info, pagination, etc. */
1133 createEvt("result", "recordReceived");                  /* f(mvr, pagePosition, isMr) : display the record*/
1134 createEvt("result", "recordDrawn");                             /* f(recordid, linkDOMNode) : after record is drawn, allow others (xul) to plugin actions */
1135 createEvt("result", "copyCountsReceived");      /* f(mvr, pagePosition, copyCountInfo) : display copy counts*/
1136 createEvt("result", "allRecordsReceived");      /* f(mvrsArray) : add other page stuff, sidebars, etc.*/
1137
1138 createEvt("rdetail", "recordDrawn");                    /* f() : the record has been drawn */
1139
1140 createEvt("common", "loggedIn");                                        /* f() : user has just logged in */
1141 createEvt('result', 'zeroHits');
1142 createEvt('result', 'lowHits');
1143 createEvt('rdetail', 'recordRetrieved');                        /* we are about to draw the rdetail page */
1144 createEvt('common', 'depthChanged');
1145 createEvt('common', 'holdUpdated'); 
1146 createEvt('common', 'holdUpdateCanceled'); 
1147
1148 createEvt('rdetail', 'nextPrevDrawn');
1149
1150
1151
1152
1153
1154 function CGI() {
1155         /* load up the url parameters */
1156
1157         this._keys = new Array();
1158         this.data = new Object();
1159
1160         var string = location.search.replace(/^\?/,"");
1161         this.server_name = location.href.replace(/^https?:\/\/([^\/]+).+$/,"$1");
1162
1163         var key = ""; 
1164         var value = "";
1165         var inkey = true;
1166         var invalue = false;
1167
1168         for( var idx = 0; idx!= string.length; idx++ ) {
1169
1170                 var c = string.charAt(idx);
1171
1172                 if( c == "=" )  {
1173                         invalue = true;
1174                         inkey = false;
1175                         continue;
1176                 } 
1177
1178                 if(c == "&" || c == ";") {
1179                         inkey = 1;
1180                         invalue = 0;
1181                         if( ! this.data[key] ) this.data[key] = [];
1182                         this.data[key].push(decodeURIComponent(value));
1183                         this._keys.push(key);
1184                         key = ""; value = "";
1185                         continue;
1186                 }
1187
1188                 if(inkey) key += c;
1189                 else if(invalue) value += c;
1190         }
1191
1192         if( ! this.data[key] ) this.data[key] = [];
1193         this.data[key].push(decodeURIComponent(value));
1194         this._keys.push(key);
1195 }
1196
1197 /* returns the value for the given param.  If there is only one value for the
1198    given param, it returns that value.  Otherwise it returns an array of values
1199  */
1200 CGI.prototype.param = function(p) {
1201         if(this.data[p] == null) return null;
1202         if(this.data[p].length == 1)
1203                 return this.data[p][0];
1204         return this.data[p];
1205 }
1206
1207 /* returns an array of param names */
1208 CGI.prototype.keys = function() {
1209         return this._keys;
1210 }
1211
1212 /* debuggin method */
1213 CGI.prototype.toString = function() {
1214         var string = "";
1215         var keys = this.keys();
1216
1217         for( var k in keys ) {
1218                 string += keys[k] + " : ";
1219                 var params = this.param(keys[k]);
1220
1221                 for( var p in params ) {
1222                         string +=  params[p] + " ";
1223                 }
1224                 string += "\n";
1225         }
1226         return string;
1227 }
1228
1229
1230 // HTTP.Cookies - Burak Gürsoy <burak[at]cpan[dot]org>
1231
1232 /*
1233 I removed all the docs (except author and license info) to reduce download size
1234 -bill erickson <billserickson@gmail.com>
1235 */
1236
1237 if (!HTTP) var HTTP = {}; // create base class if undefined
1238
1239 HTTP.Cookies = function () { // HTTP.Cookies constructor
1240    this.JAR = ''; // data cache
1241 }
1242
1243 HTTP.Cookies.VERSION = '1.01';
1244
1245 HTTP.Cookies.Date = function () { // expire time calculation class
1246    this.format = {
1247    's' : 1,
1248    'm' : 60,
1249    'h' : 60 * 60,
1250    'd' : 60 * 60 * 24,
1251    'M' : 60 * 60 * 24 * 30,
1252    'y' : 60 * 60 * 24 * 365
1253    };
1254 }
1255
1256 HTTP.Cookies.Date.prototype.parse = function (x) {
1257    if(!x || x == 'now') return 0;
1258    var date = x.match(/^(.+?)(\w)$/i);
1259    var of = 0;
1260    return (this.is_num(date[1]) && (of = this.is_date(date[1],date[2]))) ? of : 0;
1261 }
1262
1263 HTTP.Cookies.Date.prototype.is_date = function (num, x) {
1264    if (!x || x.length != 1) return 0;
1265    var ar = [];
1266    return (ar = x.match(/^(s|m|h|d|w|M|y)$/) ) ? num * 1000 * this.format[ar[0]] : 0;
1267 }
1268
1269 HTTP.Cookies.Date.prototype.is_num = function (x) {
1270    if (x.length == 0) return;
1271    var ok = 1;
1272    for (var i = 0; i < x.length; i++) {
1273       if ("0123456789.-+".indexOf(x.charAt(i)) == -1) {
1274          ok--;
1275          break;
1276       }
1277    }
1278    return ok;
1279 }
1280
1281 HTTP.Cookies.prototype.date = new HTTP.Cookies.Date; // date object instance
1282
1283 // Get the value of the named cookie. Usage: password = cookie.read('password');
1284 HTTP.Cookies.prototype.read = function (name) {
1285    var value  = '';
1286    if(!this.JAR) {
1287                 this.JAR = {};
1288       var array  = document.cookie.split(';');
1289       for (var x = 0; x < array.length; x++) {
1290          var pair = array[x].split('=');
1291          if(pair[0].substring (0,1) == ' ') pair[0] = pair[0].substring(1, pair[0].length);
1292          if(pair[0] == name) {
1293             value = pair[1];
1294          }
1295          this.JAR[pair[0]] = pair[1];
1296       }
1297    } else {
1298       for(var cookie in this.JAR) {
1299          if(cookie == name) {
1300             value = this.JAR[cookie];
1301          }
1302            }
1303    }
1304    return value ? unescape(value) : '';
1305 }
1306
1307 // Create a new cookie or overwrite existing. Usage: cookie.write('password', 'secret', '1m');
1308 HTTP.Cookies.prototype.write = function (name, value, expires, path, domain, secure) {
1309    var extra = '';
1310    if (!expires) expires = '';
1311    if (expires == '_epoch') {
1312       expires = new Date(0);
1313    } else if (expires != -1) {
1314       var Now  = new Date;
1315       Now.setTime(Now.getTime() + this.date.parse(expires));
1316       expires = Now.toGMTString();
1317    }
1318    if(expires != -1 && expires) extra += "; expires=" + expires;
1319    if(path   ) extra += "; path="    + path;
1320    if(domain ) extra += "; domain="  + domain;
1321    if(secure ) extra += "; secure="  + secure;
1322    document.cookie = name + "=" + escape(value) + extra;
1323 }
1324
1325 // Delete the named cookie. Usage: cookie.remove('password');
1326 HTTP.Cookies.prototype.remove = function (name, path, domain, secure) {
1327    this.write(name, '', '_epoch', path, domain, secure);
1328 }
1329
1330 /*
1331
1332 =head1 NAME
1333
1334 HTTP.Cookies - JavaScript class for reading, writing and deleting cookies
1335
1336 =head1 AUTHOR
1337
1338 Burak Gürsoy, E<lt>burakE<64>cpan.orgE<gt>
1339
1340 =head1 COPYRIGHT
1341
1342 Copyright 2005 Burak Gürsoy. All rights reserved.
1343
1344 =head1 LICENSE
1345
1346 This library is free software; you can redistribute it and/or modify
1347 it under the terms of the "Artistic License":
1348 L<http://dev.perl.org/licenses/artistic.html>.
1349
1350 =cut
1351
1352 */
1353 /*
1354  * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
1355  * Digest Algorithm, as defined in RFC 1321.
1356  * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
1357  * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
1358  * Distributed under the BSD License
1359  * See http://pajhome.org.uk/crypt/md5 for more info.
1360  */
1361
1362 var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
1363 var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
1364 var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
1365 function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
1366 function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
1367 function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
1368 function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
1369 function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
1370 function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
1371 function md5_vm_test()
1372 {
1373   return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
1374 }
1375 function core_md5(x, len)
1376 {
1377   /* append padding */
1378   x[len >> 5] |= 0x80 << ((len) % 32);
1379   x[(((len + 64) >>> 9) << 4) + 14] = len;
1380
1381   var a =  1732584193;
1382   var b = -271733879;
1383   var c = -1732584194;
1384   var d =  271733878;
1385
1386   for(var i = 0; i < x.length; i += 16)
1387   {
1388     var olda = a;
1389     var oldb = b;
1390     var oldc = c;
1391     var oldd = d;
1392
1393     a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
1394     d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
1395     c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
1396     b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
1397     a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
1398     d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
1399     c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
1400     b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
1401     a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
1402     d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
1403     c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
1404     b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
1405     a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
1406     d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
1407     c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
1408     b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
1409
1410     a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
1411     d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
1412     c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
1413     b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
1414     a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
1415     d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
1416     c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
1417     b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
1418     a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
1419     d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
1420     c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
1421     b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
1422     a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
1423     d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
1424     c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
1425     b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
1426
1427     a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
1428     d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
1429     c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
1430     b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
1431     a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
1432     d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
1433     c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
1434     b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
1435     a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
1436     d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
1437     c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
1438     b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
1439     a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
1440     d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
1441     c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
1442     b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
1443
1444     a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
1445     d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
1446     c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
1447     b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
1448     a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
1449     d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
1450     c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
1451     b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
1452     a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
1453     d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
1454     c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
1455     b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
1456     a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
1457     d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
1458     c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
1459     b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
1460
1461     a = safe_add(a, olda);
1462     b = safe_add(b, oldb);
1463     c = safe_add(c, oldc);
1464     d = safe_add(d, oldd);
1465   }
1466   return Array(a, b, c, d);
1467
1468 }
1469 function md5_cmn(q, a, b, x, s, t)
1470 {
1471   return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
1472 }
1473 function md5_ff(a, b, c, d, x, s, t)
1474 {
1475   return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
1476 }
1477 function md5_gg(a, b, c, d, x, s, t)
1478 {
1479   return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
1480 }
1481 function md5_hh(a, b, c, d, x, s, t)
1482 {
1483   return md5_cmn(b ^ c ^ d, a, b, x, s, t);
1484 }
1485 function md5_ii(a, b, c, d, x, s, t)
1486 {
1487   return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
1488 }
1489 function core_hmac_md5(key, data)
1490 {
1491   var bkey = str2binl(key);
1492   if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
1493
1494   var ipad = Array(16), opad = Array(16);
1495   for(var i = 0; i < 16; i++)
1496   {
1497     ipad[i] = bkey[i] ^ 0x36363636;
1498     opad[i] = bkey[i] ^ 0x5C5C5C5C;
1499   }
1500
1501   var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
1502   return core_md5(opad.concat(hash), 512 + 128);
1503 }
1504 function safe_add(x, y)
1505 {
1506   var lsw = (x & 0xFFFF) + (y & 0xFFFF);
1507   var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
1508   return (msw << 16) | (lsw & 0xFFFF);
1509 }
1510 function bit_rol(num, cnt)
1511 {
1512   return (num << cnt) | (num >>> (32 - cnt));
1513 }
1514 function str2binl(str)
1515 {
1516   var bin = Array();
1517   var mask = (1 << chrsz) - 1;
1518   for(var i = 0; i < str.length * chrsz; i += chrsz)
1519     bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
1520   return bin;
1521 }
1522 function binl2str(bin)
1523 {
1524   var str = "";
1525   var mask = (1 << chrsz) - 1;
1526   for(var i = 0; i < bin.length * 32; i += chrsz)
1527     str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
1528   return str;
1529 }
1530 function binl2hex(binarray)
1531 {
1532   var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
1533   var str = "";
1534   for(var i = 0; i < binarray.length * 4; i++)
1535   {
1536     str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
1537            hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
1538   }
1539   return str;
1540 }
1541 function binl2b64(binarray)
1542 {
1543   var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1544   var str = "";
1545   for(var i = 0; i < binarray.length * 4; i += 3)
1546   {
1547     var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
1548                 | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
1549                 |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
1550     for(var j = 0; j < 4; j++)
1551     {
1552       if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
1553       else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
1554     }
1555   }
1556   return str;
1557 }
1558 /*
1559 var stpicopen   = '../../../../images/slimtree/folder.gif';
1560 var stpicclose = '../../../../images/slimtree/folderopen.gif';
1561 */
1562 var stpicopen   = '../../../../images/slimtree/folder2.gif';
1563 var stpicclose = '../../../../images/slimtree/folderopen2.gif';
1564 var stpicblank = '../../../../images/slimtree/page.gif';
1565 var stpicline   = '../../../../images/slimtree/line.gif';
1566 var stpicjoin   = '../../../../images/slimtree/join.gif';
1567 var stpicjoinb = '../../../../images/slimtree/joinbottom.gif';
1568
1569 var stimgopen;
1570 var stimgclose;
1571 var stimgblank;
1572 var stimgline;
1573 var stimgjoin;
1574
1575 function _apc(root,node) { root.appendChild(node); }
1576
1577 function SlimTree(context, handle, rootimg) { 
1578         
1579         if(!stimgopen) {
1580                 stimgopen       = elem('img',{src:stpicopen,border:0, style:'height:13px;width:31px;'});
1581                 stimgclose      = elem('img',{src:stpicclose,border:0, style:'height:13px;width:31px;'});
1582                 stimgblank      = elem('img',{src:stpicblank,border:0, style:'height:18px;width:18px;'});
1583                 stimgline       = elem('img',{src:stpicline,border:0, style:'height:18px;width:18px;'});
1584                 stimgjoin       = elem('img',{src:stpicjoin,border:0, style:'display:inline;height:18px;width:18px;'});
1585         }
1586
1587         this.context    = context; 
1588         this.handle             = handle;
1589         this.cache              = new Object();
1590         if(rootimg) 
1591                 this.rootimg = elem('img', 
1592                         {src:rootimg,border:0,style:'padding-right: 4px;'});
1593 }
1594
1595 SlimTree.prototype.addCachedChildren = function(pid) {
1596         var child;
1597         while( child = this.cache[pid].shift() ) 
1598                 this.addNode( child.id, child.pid, 
1599                         child.name, child.action, child.title );
1600         this.cache[pid] = null;
1601 }
1602
1603 SlimTree.prototype.addNode = function( id, pid, name, action, title, cls ) {
1604
1605         if( pid != -1 && !$(pid)) {
1606                 if(!this.cache[pid]) this.cache[pid] = new Array();
1607                 this.cache[pid].push(
1608                         {id:id,pid:pid,name:name,action:action,title:title });
1609                 return;
1610         }
1611
1612         if(!action)
1613                 action='javascript:'+this.handle+'.toggle("'+id+'");';
1614
1615         var actionref;
1616         if( typeof action == 'string' )
1617                 actionref = elem('a',{href:action}, name);
1618         else {
1619                 actionref = elem('a',{href:'javascript:void(0);'}, name);
1620                 actionref.onclick = action;
1621         }
1622
1623         var div                 = elem('div',{id:id});
1624         var topdiv              = elem('div',{style:'vertical-align:middle'});
1625         var link                        = elem('a', {id:'stlink_' + id}); 
1626         var contdiv             = elem('div',{id:'stcont_' + id});
1627
1628         if(cls) addCSSClass(actionref, cls);
1629
1630         //actionref.setAttribute('href',action);
1631         if(title) actionref.setAttribute('title',title);
1632         else actionref.setAttribute('title',name);
1633
1634         _apc(topdiv,link);
1635         _apc(topdiv,actionref);
1636         _apc(div,topdiv);
1637         _apc(div,contdiv);
1638
1639         if( pid == -1 ) { 
1640
1641                 this.rootid = id;
1642                 _apc(this.context,div);
1643                 if(this.rootimg) _apc(link,this.rootimg.cloneNode(true));
1644                 else _apc(link,stimgblank.cloneNode(true));
1645
1646         } else {
1647
1648                 if(pid == this.rootid) this.open(pid);
1649                 else this.close(pid);
1650                 $(pid).setAttribute('haschild','1');
1651                 _apc(link,stimgblank.cloneNode(true));
1652                 div.style.paddingLeft = '18px';
1653                 div.style.backgroundImage = 'url('+stpicjoinb+')';
1654                 div.style.backgroundRepeat = 'no-repeat';
1655                 _apc($('stcont_' + pid), div);
1656                 if (div.previousSibling) stMakePaths(div);
1657         }
1658         if(this.cache[id]) this.addCachedChildren(id);
1659 }
1660
1661 function stMakePaths(div) {
1662         _apc(div.previousSibling.firstChild,stimgjoin.cloneNode(true));
1663         _apc(div.previousSibling.firstChild,div.previousSibling.firstChild.firstChild);
1664         _apc(div.previousSibling.firstChild,div.previousSibling.firstChild.firstChild);
1665         div.previousSibling.firstChild.firstChild.style.marginLeft = '-18px';
1666         div.previousSibling.style.backgroundImage = 'url('+stpicline+')';
1667         div.previousSibling.style.backgroundRepeat = 'repeat-y';
1668 }
1669
1670 SlimTree.prototype.expandAll = function() { this.flex(this.rootid, 'open'); }
1671 SlimTree.prototype.closeAll = function() { this.flex(this.rootid,'close'); }
1672 SlimTree.prototype.flex = function(id, type) {
1673         if(type=='open') this.open(id);
1674         else { if (id != this.rootid) this.close(id); }
1675         var n = $('stcont_' + id);
1676         for( var c = 0; c != n.childNodes.length; c++ ) {
1677                 var ch = n.childNodes[c];
1678                 if(ch.nodeName.toLowerCase() == 'div') {
1679                         if($(ch.id).getAttribute('haschild') == '1') 
1680                                 this.flex(ch.id, type);
1681                 }
1682         }
1683 }
1684
1685 SlimTree.prototype.toggle = function(id) {
1686         if($(id).getAttribute('ostate') == '1') this.open(id);
1687         else if($(id).getAttribute('ostate') == '2') this.close(id);
1688 }
1689
1690 SlimTree.prototype.open = function(id) {
1691         if($(id).getAttribute('ostate') == '2') return;
1692         var link = $('stlink_' + id);
1693         if(!link) return;
1694         if(id != this.rootid || !this.rootimg) {
1695                 removeChildren(link);
1696                 _apc(link,stimgclose.cloneNode(true));
1697         }
1698         link.setAttribute('href','javascript:' + this.handle + '.close("'+id+'");');
1699         unHideMe($('stcont_' + id));
1700         $(id).setAttribute('ostate','2');
1701 }
1702
1703 SlimTree.prototype.close = function(id) {
1704         var link = $('stlink_' + id);
1705         if(!link) return;
1706         if(id != this.rootid || !this.rootimg) {
1707                 removeChildren(link);
1708                 _apc(link,stimgopen.cloneNode(true));
1709         }
1710         link.setAttribute('href','javascript:' + this.handle + '.open("'+id+'");');
1711         hideMe($('stcont_' + id));
1712         $(id).setAttribute('ostate','1');
1713 }
1714
1715 /* - Request ------------------------------------------------------------- */
1716
1717
1718 /* define it again here for pages that don't load RemoteRequest */
1719 function isXUL() { try { if(IAMXUL) return true;}catch(e){return false;}; }
1720
1721
1722 var cookieManager = new HTTP.Cookies();
1723
1724 var __ilsEvent; /* the last event the occurred */
1725
1726 var DEBUGSLIM;
1727 function Request(type) {
1728
1729         var s = type.split(":");
1730         if(s[2] == "1" && isXUL()) s[1] += ".staff";
1731         this.request = new RemoteRequest(s[0], s[1]);
1732         var p = [];
1733
1734         if(isXUL()) {
1735                 if(!location.href.match(/^https:/))
1736                         this.request.setSecure(false);
1737
1738         } else {
1739
1740                 if( G.user && G.user.session ) {
1741                         /* if the user is logged in, all activity resets the timeout 
1742                                 This is not entirely accurate in the sense that not all 
1743                                 requests will reset the server timeout - this should
1744                                 get close enough, however.
1745                         */
1746                         var at = getAuthtime();
1747                         if(at) new AuthTimer(at).run(); 
1748                 }
1749         }
1750
1751         for( var x = 1; x!= arguments.length; x++ ) {
1752                 p.push(arguments[x]);
1753                 this.request.addParam(arguments[x]);
1754         }
1755
1756         if( getDebug() ) {
1757                 var str = "";
1758                 for( var i = 0; i != p.length; i++ ) {
1759                         if( i > 0 ) str += ", "
1760                         str += js2JSON(p[i]);
1761                 }
1762                 _debug('request ' + s[0] + ' ' + s[1] + ' ' + str );
1763
1764         } else if( DEBUGSLIM ) {
1765                 _debug('request ' + s[1]);
1766         }
1767 }
1768
1769 Request.prototype.callback = function(cal) {this.request.setCompleteCallback(cal);}
1770 Request.prototype.send          = function(block){this.request.send(block);}
1771 Request.prototype.result        = function(){return this.request.getResultObject();}
1772
1773 function showCanvas() {
1774         for( var x in G.ui.altcanvas ) {
1775                 hideMe(G.ui.altcanvas[x]);
1776         }
1777         hideMe(G.ui.common.loading);
1778         unHideMe(G.ui.common.canvas_main);
1779         try{G.ui.searchbar.text.focus();}catch(e){}
1780 }
1781
1782 function swapCanvas(newNode) {
1783         for( var x in G.ui.altcanvas ) 
1784                 hideMe(G.ui.altcanvas[x]);
1785
1786         hideMe(G.ui.common.loading);
1787         hideMe(G.ui.common.canvas_main);
1788         unHideMe(newNode);
1789 }
1790
1791 /* finds the name of the current page */
1792 var currentPage = null;
1793 function findCurrentPage() {
1794         if(currentPage) return currentPage;
1795
1796         var pages = [];
1797         for( var p in config.page ) pages.push(config.page[p]);
1798         pages = pages.sort( function(a,b){ return - (a.length - b.length); } );
1799
1800         var path = location.pathname;
1801         if(!path.match(/.*\.xml$/))
1802                 path += "index.xml"; /* in case they go to  / */
1803
1804         var page = null;
1805         for( var p = 0; p < pages.length; p++ ) {
1806                 if( path.indexOf(pages[p]) != -1)
1807                         page = pages[p];
1808         }
1809
1810         for( var p in config.page ) {
1811                 if(config.page[p] == page) {
1812                         currentPage = p;
1813                         return p;
1814                 }
1815         }
1816         return null;
1817 }
1818
1819
1820 /* sets all of the params values  ----------------------------- */
1821 function initParams() {
1822         var cgi = new CGI();    
1823
1824         /* handle the location var */
1825         var org;
1826         var loc = cgi.param(PARAM_LOCATION);
1827         var lasso = cgi.param(PARAM_LASSO);
1828
1829     if ( lasso ) {
1830                 lasso = findOrgLasso( lasso );
1831                 LASSO = lasso ? lasso.id() : null;
1832         }
1833
1834     if (loc) {
1835                 org = findOrgUnit(loc);
1836                 LOCATION = org ? org.id() : null;
1837
1838                 if ( !LOCATION ){
1839                         org = findOrgUnit(loc);
1840                         LOCATION = org ? org.id() : null;
1841                 }
1842     }
1843
1844         org = null;
1845         loc = cgi.param(PARAM_ORIGLOC);
1846         if( loc ) {
1847                 org = findOrgUnit(loc);
1848                 if(!org) org = findOrgUnitSN(loc);
1849         }
1850         ORIGLOC = (org) ? org.id() : null;
1851
1852
1853         DEPTH = parseInt(cgi.param(PARAM_DEPTH));
1854         if(isNaN(DEPTH)) DEPTH = null;
1855
1856
1857         FACET           = cgi.param(PARAM_FACET);
1858         TERM            = cgi.param(PARAM_TERM);
1859         STYPE           = cgi.param(PARAM_STYPE);
1860         FORM            = cgi.param(PARAM_FORM);
1861         //DEPTH         = parseInt(cgi.param(PARAM_DEPTH));
1862         OFFSET  = parseInt(cgi.param(PARAM_OFFSET));
1863         COUNT           = parseInt(cgi.param(PARAM_COUNT));
1864         HITCOUNT        = parseInt(cgi.param(PARAM_HITCOUNT));
1865         MRID            = parseInt(cgi.param(PARAM_MRID));
1866         RID             = parseInt(cgi.param(PARAM_RID));
1867         AUTHTIME        = parseInt(cgi.param(PARAM_AUTHTIME));
1868         ADVTERM = cgi.param(PARAM_ADVTERM);
1869         ADVTYPE = cgi.param(PARAM_ADVTYPE);
1870         RTYPE           = cgi.param(PARAM_RTYPE);
1871         SORT            = cgi.param(PARAM_SORT);
1872         SORT_DIR        = cgi.param(PARAM_SORT_DIR);
1873         DEBUG           = cgi.param(PARAM_DEBUG);
1874         CALLNUM = cgi.param(PARAM_CN);
1875         LITFORM = cgi.param(PARAM_LITFORM);
1876         ITEMFORM        = cgi.param(PARAM_ITEMFORM);
1877         ITEMTYPE        = cgi.param(PARAM_ITEMTYPE);
1878         BIBLEVEL        = cgi.param(PARAM_BIBLEVEL);
1879         AUDIENCE        = cgi.param(PARAM_AUDIENCE);
1880         SEARCHES = cgi.param(PARAM_SEARCHES);
1881         LANGUAGE        = cgi.param(PARAM_LANGUAGE);
1882         TFORM           = cgi.param(PARAM_TFORM);
1883         RDEPTH  = cgi.param(PARAM_RDEPTH);
1884     AVAIL   = cgi.param(PARAM_AVAIL);
1885     COPYLOCS   = cgi.param(PARAM_COPYLOCS);
1886     PUBD_BEFORE = cgi.param(PARAM_PUBD_BEFORE);
1887     PUBD_AFTER = cgi.param(PARAM_PUBD_AFTER);
1888     PUBD_BETWEEN = cgi.param(PARAM_PUBD_BETWEEN);
1889     PUBD_DURING = cgi.param(PARAM_PUBD_DURING);
1890
1891     
1892         /* set up some sane defaults */
1893         //if(isNaN(DEPTH))      DEPTH           = 0;
1894         if(isNaN(RDEPTH))       RDEPTH  = 0;
1895         if(isNaN(OFFSET))       OFFSET  = 0;
1896         if(isNaN(COUNT))        COUNT           = 10;
1897         if(isNaN(HITCOUNT))     HITCOUNT        = 0;
1898         if(isNaN(MRID))         MRID            = 0;
1899         if(isNaN(RID))          RID             = 0;
1900         if(isNaN(ORIGLOC))      ORIGLOC = 0; /* so we know it hasn't been set */
1901         if(isNaN(AUTHTIME))     AUTHTIME        = 0;
1902         if(ADVTERM==null)       ADVTERM = "";
1903     if(isNaN(AVAIL))    AVAIL = 0;
1904 }
1905
1906 function clearSearchParams() {
1907         TERM        = null;
1908         STYPE       = null;
1909         FORM        = null;
1910         OFFSET      = 0;
1911         HITCOUNT    = 0;  
1912         ADVTERM     = null;
1913         ADVTYPE     = null;
1914         MRID        = null;
1915         RID         = null;
1916         RTYPE       = null;
1917         SORT        = null;
1918         SORT_DIR    = null;
1919         RLIST       = null;
1920         CALLNUM     = null;
1921         LITFORM     = null;
1922         ITEMFORM    = null;
1923         ITEMTYPE    = null;
1924         BIBLEVEL    = null;
1925         AUDIENCE    = null;
1926         SEARCHES    = null;
1927         LANGUAGE    = null;
1928         RDEPTH      = null;
1929     AVAIL       = null;
1930     COPYLOCS    = null;
1931     PUBD_BEFORE = null;
1932     PUBD_AFTER  = null;
1933     PUBD_BETWEEN = null;
1934     PUBD_DURING = null;
1935 }
1936
1937
1938 function initCookies() {
1939         FONTSIZE = "regular";
1940         var font = cookieManager.read(COOKIE_FONT);
1941         scaleFonts(font);
1942         if(font) FONTSIZE = font;
1943         SKIN = cookieManager.read(COOKIE_SKIN);
1944     if(findCurrentPage() == HOME)
1945         cookieManager.remove(COOKIE_SEARCH);
1946 }
1947
1948 /* URL param accessors */
1949 function getTerm(){return TERM;}
1950 function getFacet(){return FACET;}
1951 function getStype(){return STYPE;}
1952 function getLocation(){return LOCATION;}
1953 function getLasso(){return LASSO;}
1954 function getDepth(){return DEPTH;}
1955 function getForm(){return FORM;}
1956 function getTform(){return TFORM;}
1957 function getOffset(){return OFFSET;}
1958 function getDisplayCount(){return COUNT;}
1959 function getHitCount(){return HITCOUNT;}
1960 function getMrid(){return MRID;};
1961 function getRid(){return RID;};
1962 function getOrigLocation(){return ORIGLOC;}
1963 function getAuthtime() { return AUTHTIME; }
1964 function getSearchBarExtras(){return SBEXTRAS;}
1965 function getFontSize(){return FONTSIZE;};
1966 function getSkin(){return SKIN;};
1967 function getAdvTerm(){return ADVTERM;}
1968 function getAdvType(){return ADVTYPE;}
1969 function getRtype(){return RTYPE;}
1970 function getSort(){return SORT;}
1971 function getSortDir(){return SORT_DIR;}
1972 function getDebug(){return DEBUG;}
1973 function getCallnumber() { return CALLNUM; }
1974 function getLitForm() { return LITFORM; }
1975 function getItemForm() { return ITEMFORM; }
1976 function getItemType() { return ITEMTYPE; }
1977 function getBibLevel() { return BIBLEVEL; }
1978 function getAudience() { return AUDIENCE; }
1979 function getSearches() { return SEARCHES; }
1980 function getLanguage() { return LANGUAGE; }
1981 function getRdepth() { return RDEPTH; }
1982 function getAvail() { return AVAIL; }
1983 function getCopyLocs() { return COPYLOCS; }
1984 function getPubdBefore() { return PUBD_BEFORE; }
1985 function getPubdAfter() { return PUBD_AFTER; }
1986 function getPubdBetween() { return PUBD_BETWEEN; }
1987 function getPubdDuring() { return PUBD_DURING; }
1988
1989
1990 function findBasePath() {
1991         var path = location.pathname;
1992         if(!path.match(/.*\.xml$/)) path += "index.xml"; 
1993         var idx = path.indexOf(config.page[findCurrentPage()]);
1994         return path.substring(0, idx);
1995 }
1996
1997 function findBaseURL(ssl) {
1998         var path = findBasePath();
1999         var proto = (ssl) ? "https:" : "http:";
2000
2001         /* strip port numbers.  This is necessary for browsers that
2002         send an explicit  <host>:80, 443 - explicit ports
2003         break links that need to change ports (e.g. http -> https) */
2004         var h = location.host.replace(/:.*/,''); 
2005
2006         return proto + "//" + h + path;
2007 }
2008
2009 /*
2010 function buildISBNSrc(isbn) {
2011         return "http://" + location.host + "/jackets/" + isbn;
2012 }
2013 */
2014
2015 function buildImageLink(name, ssl) {
2016         return findBaseURL(ssl) + "../../../../images/" + name;
2017 }
2018
2019 function buildExtrasLink(name, ssl) {
2020         return findBaseURL(ssl) + "../../../../extras/" + name;
2021 }
2022
2023 var consoleService;
2024 function _debug(str) { 
2025         try { dump('dbg: ' + str + '\n'); } catch(e) {} 
2026
2027         /* potentially useful, but usually just annoying */
2028         /*
2029         if(!IE) {
2030                 if(!consoleService) {
2031                         try {
2032                                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
2033                                 this.consoleService = Components.classes['@mozilla.org/consoleservice;1']
2034                                         .getService(Components.interfaces.nsIConsoleService);
2035                         } catch(e) {}
2036                 }
2037         
2038                 try {
2039                         if(consoleService) {
2040                                 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
2041                                 consoleService.logStringMessage(str + '\n');
2042                         }
2043                 } catch(e){}
2044         }
2045         */
2046 }
2047
2048 var forceLoginSSL; // set via Apache env variable
2049 function  buildOPACLink(args, slim, ssl) {
2050
2051         if(!args) args = {};
2052         var string = "";
2053
2054     if( ssl == undefined && (
2055             location.protocol == 'https:' ||
2056             (forceLoginSSL && G.user && G.user.session))) {
2057         ssl = true;
2058     }
2059
2060         if(!slim) {
2061                 string = findBaseURL(ssl);
2062                 if(args.page) string += config.page[args.page];
2063                 else string += config.page[findCurrentPage()];
2064         }
2065
2066         /* this may seem unnecessary.. safety precaution for now */
2067         /*
2068         if( args[PARAM_DEPTH] == null )
2069                 args[PARAM_DEPTH] = getDepth();
2070                 */
2071
2072         string += "?";
2073
2074         for( var x in args ) {
2075                 var v = args[x];
2076                 if(x == "page" || v == null || v == undefined || v+'' == 'NaN' ) continue;
2077                 if(x == PARAM_OFFSET && v == 0) continue;
2078                 if(x == PARAM_COUNT && v == 10) continue;
2079                 if(x == PARAM_FORM && v == 'all' ) continue;
2080                 if( instanceOf(v, Array) && v.length ) {
2081                         for( var i = 0; i < v.length; i++ ) {
2082                                 string += "&" + x + "=" + encodeURIComponent(v[i]);
2083                         }
2084                 } else {
2085                         string += "&" + x + "=" + encodeURIComponent(v);
2086                 }
2087         }
2088
2089         if(getDebug())
2090                 string += _appendParam(DEBUG,           PARAM_DEBUG, args, getDebug, string);
2091         if(getOrigLocation() != 1) 
2092                 string += _appendParam(ORIGLOC, PARAM_ORIGLOC, args, getOrigLocation, string);
2093         if(getTerm()) 
2094                 string += _appendParam(TERM,            PARAM_TERM, args, getTerm, string);
2095         if(getFacet()) 
2096                 string += _appendParam(FACET,           PARAM_FACET, args, getFacet, string);
2097         if(getStype()) 
2098                 string += _appendParam(STYPE,           PARAM_STYPE, args, getStype, string);
2099         if(getLocation() != 1) 
2100                 string += _appendParam(LOCATION, PARAM_LOCATION, args, getLocation, string);
2101         if(getLasso() != null) 
2102                 string += _appendParam(LASSO, PARAM_LASSO, args, getLasso, string);
2103         if(getDepth() != null) 
2104                 string += _appendParam(DEPTH,           PARAM_DEPTH, args, getDepth, string);
2105         if(getForm() && (getForm() != 'all') ) 
2106                 string += _appendParam(FORM,            PARAM_FORM, args, getForm, string);
2107         if(getTform() && (getTform() != 'all') ) 
2108                 string += _appendParam(TFORM,           PARAM_TFORM, args, getTform, string);
2109         if(getOffset() != 0) 
2110                 string += _appendParam(OFFSET,  PARAM_OFFSET, args, getOffset, string);
2111         if(getDisplayCount() != 10) 
2112                 string += _appendParam(COUNT,           PARAM_COUNT, args, getDisplayCount, string);
2113         if(getHitCount()) 
2114                 string += _appendParam(HITCOUNT, PARAM_HITCOUNT, args, getHitCount, string);
2115         if(getMrid())
2116                 string += _appendParam(MRID,            PARAM_MRID, args, getMrid, string);
2117         if(getRid())
2118                 string += _appendParam(RID,             PARAM_RID, args, getRid, string);
2119         if(getAuthtime())
2120                 string += _appendParam(AUTHTIME,        PARAM_AUTHTIME, args, getAuthtime, string);
2121         if(getAdvTerm())
2122                 string += _appendParam(ADVTERM, PARAM_ADVTERM, args, getAdvTerm, string);
2123         if(getAdvType())
2124                 string += _appendParam(ADVTYPE, PARAM_ADVTYPE, args, getAdvType, string);
2125         if(getRtype())
2126                 string += _appendParam(RTYPE,           PARAM_RTYPE, args, getRtype, string);
2127         if(getItemForm())
2128                 string += _appendParam(ITEMFORM,        PARAM_ITEMFORM, args, getItemForm, string);
2129         if(getItemType())
2130                 string += _appendParam(ITEMTYPE,        PARAM_ITEMTYPE, args, getItemType, string);
2131         if(getBibLevel())
2132                 string += _appendParam(BIBLEVEL,        PARAM_BIBLEVEL, args, getBibLevel, string);
2133         if(getLitForm())
2134                 string += _appendParam(LITFORM, PARAM_LITFORM, args, getLitForm, string);
2135         if(getAudience())
2136                 string += _appendParam(AUDIENCE,        PARAM_AUDIENCE, args, getAudience, string);
2137         if(getSearches())
2138                 string += _appendParam(SEARCHES,        PARAM_SEARCHES, args, getSearches, string);
2139         if(getLanguage())
2140                 string += _appendParam(LANGUAGE,        PARAM_LANGUAGE, args, getLanguage, string);
2141         if(getRdepth() != null)
2142                 string += _appendParam(RDEPTH,  PARAM_RDEPTH, args, getRdepth, string);
2143         if(getSort() != null)
2144                 string += _appendParam(SORT,    PARAM_SORT, args, getSort, string);
2145         if(getSortDir() != null)
2146                 string += _appendParam(SORT_DIR,        PARAM_SORT_DIR, args, getSortDir, string);
2147         if(getAvail())
2148                 string += _appendParam(AVAIL, PARAM_AVAIL, args, getAvail, string);
2149         if(getCopyLocs())
2150                 string += _appendParam(COPYLOCS, PARAM_COPYLOCS, args, getCopyLocs, string);
2151     if(getPubdBefore())
2152                 string += _appendParam(PUBD_BEFORE, PARAM_PUBD_BEFORE, args, getPubdBefore, string);
2153     if(getPubdAfter())
2154                 string += _appendParam(PUBD_AFTER, PARAM_PUBD_AFTER, args, getPubdAfter, string);
2155     if(getPubdBetween())
2156                 string += _appendParam(PUBD_BETWEEN, PARAM_PUBD_BETWEEN, args, getPubdBetween, string);
2157     if(getPubdDuring())
2158                 string += _appendParam(PUBD_DURING, PARAM_PUBD_DURING, args, getPubdDuring, string);
2159
2160
2161         return string.replace(/\&$/,'').replace(/\?\&/,"?");    
2162 }
2163
2164 var xx = 1;
2165 function _appendParam( fieldVar, fieldName, overrideArgs, getFunc, string ) {
2166
2167         var ret = "";
2168
2169         if(     fieldVar != null && 
2170                         (fieldVar +'' != 'NaN') && 
2171                         overrideArgs[fieldName] == null &&
2172                         getFunc() != null &&
2173                         getFunc()+'' != '' ) {
2174
2175                 ret = "&" + fieldName + "=" + encodeURIComponent(getFunc());
2176         }
2177
2178         return ret;
2179 }
2180
2181 /* ----------------------------------------------------------------------- */
2182 function cleanISBN(isbn) {
2183    if(isbn) {
2184       isbn = isbn.toString().replace(/^\s+/,"");
2185       var idx = isbn.indexOf(" ");
2186       if(idx > -1) { isbn = isbn.substring(0, idx); }
2187    } else isbn = "";
2188    return isbn;
2189 }       
2190
2191
2192 /* builds a link that goes to the title listings for a metarecord */
2193 function buildTitleLink(rec, link) {
2194         if(!rec) return;
2195         link.appendChild(text(normalize(truncate(rec.title(), 65))));
2196         var args = {};
2197         args.page = RRESULT;
2198         args[PARAM_OFFSET] = 0;
2199         args[PARAM_MRID] = rec.doc_id();
2200         args[PARAM_RTYPE] = RTYPE_MRID;
2201     var linkText = link.innerHTML; // IE
2202         link.setAttribute("href", buildOPACLink(args));
2203     link.innerHTML = linkText; // IE
2204 }
2205
2206 function buildTitleDetailLink(rec, link) {
2207         if(!rec) return;
2208         link.appendChild(text(normalize(truncate(rec.title(), 65))));
2209         var args = {};
2210         args.page = RDETAIL;
2211         args[PARAM_RID] = rec.doc_id();
2212     // in IE, if the link text contains a '@', it replaces the innerHTML text 
2213     // with the value of the href attribute.  Wait, what?  Yes.  Capture the
2214     // innerHTML and put it back into place after the href is set
2215     var linkText = link.innerHTML; // IE
2216         link.setAttribute("href", buildOPACLink(args));
2217     link.innerHTML = linkText; // IE
2218 }
2219
2220 /* 'type' is one of STYPE_AUTHOR, STYPE_SUBJECT, ... found in config.js 
2221         'trunc' is the number of characters to show in the string, defaults to 65 */
2222 function buildSearchLink(type, string, linknode, trunc) {
2223         if(!trunc) trunc = 65;
2224         var args = {};
2225         if( SHOW_MR_DEFAULT || findCurrentPage() == MRESULT ) {
2226                 args.page = MRESULT;
2227         } else {
2228                 args.page = RRESULT;
2229                 args[PARAM_RTYPE] = type;
2230         }
2231         args[PARAM_OFFSET] = 0;
2232         args[PARAM_TERM] = string;
2233         args[PARAM_STYPE] = type;
2234         linknode.appendChild(text(normalize(truncate(string, trunc))));
2235         linknode.setAttribute("href", buildOPACLink(args));
2236 }
2237
2238 function setSessionCookie(ses) {
2239         cookieManager.write(COOKIE_SES, ses, -1);
2240 }
2241
2242
2243
2244 /* ----------------------------------------------------------------------- */
2245 /* user session handling */
2246 /* ----------------------------------------------------------------------- */
2247 /* session is the login session.  If no session is provided, we attempt
2248         to find one in the cookies.  If 'force' is true we retrieve the 
2249         user from the server even if there is already a global user present.
2250         if ses != G.user.session, we also force a grab */
2251 function grabUser(ses, force) {
2252
2253     _debug("grabUser auth token = " + ses);
2254         if(!ses && isXUL()) {
2255                 stash = fetchXULStash();
2256                 ses = stash.session.key
2257                 _debug("stash auth token = " + ses);
2258         }
2259
2260         if(!ses) {
2261                 ses = cookieManager.read(COOKIE_SES);
2262                 /* https cookies don't show up in http servers.. */
2263                 _debug("cookie auth token = " + ses);
2264         }
2265
2266         if(!ses) return false;
2267
2268         if(!force) 
2269                 if(G.user && G.user.session == ses)
2270                         return G.user;
2271
2272         /* first make sure the session is valid */
2273         var request = new Request(FETCH_SESSION, ses, 1 );
2274         request.request.alertEvent = false;
2275         request.send(true);
2276         var user = request.result();
2277
2278         if(!user || user.textcode == 'NO_SESSION') {
2279
2280         if(isXUL()) {
2281             dojo.require('openils.XUL');
2282             dump('getNewSession in opac_utils.js\n');
2283             openils.XUL.getNewSession( 
2284                 function(success, authtoken) { 
2285                     if(success) {
2286                         ses = authtoken;
2287                         var request = new Request(FETCH_SESSION, ses, 1);
2288                         request.request.alertEvent = false;
2289                         request.send(true);
2290                         user = request.result();
2291                     }
2292                 }
2293             );
2294         }
2295
2296             if(!user || user.textcode == 'NO_SESSION') {
2297                     doLogout();
2298                     return false; /* unable to grab the session */
2299         }
2300         }
2301
2302         if( !(typeof user == 'object' && user._isfieldmapper) ) {
2303                 doLogout();
2304                 return false;
2305         }
2306
2307         G.user = user;
2308         G.user.fleshed = false;
2309         G.user.session = ses;
2310         setSessionCookie(ses);
2311
2312         grabUserPrefs();
2313         if(G.user.prefs['opac.hits_per_page'])
2314                 COUNT = parseInt(G.user.prefs['opac.hits_per_page']);
2315
2316         if(G.user.prefs[PREF_DEF_FONT]) 
2317                 setFontSize(G.user.prefs[PREF_DEF_FONT]);
2318
2319         var at = getAuthtime();
2320         //if(isXUL()) at = xulG['authtime'];
2321
2322         if(at && !isXUL()) new AuthTimer(at).run(); 
2323         return G.user;
2324 }
2325
2326
2327 /* sets the 'prefs' field of the user object to their preferences 
2328         and returns the preferences */
2329 function grabUserPrefs(user, force) {
2330         if(user == null) user = G.user;
2331         if(!force && user.prefs) return user.prefs;     
2332         var req = new Request(FETCH_USER_PREFS, G.user.session, user.id());
2333         req.send(true);
2334         user.prefs = req.result();
2335         return user.prefs;
2336 }
2337
2338 function grabFleshedUser() {
2339
2340         if(!G.user || !G.user.session) {
2341                 grabUser();     
2342                 if(!G.user || !G.user.session) return null;
2343         }
2344
2345         if(G.user.fleshed) return G.user;
2346
2347    var req = new Request(FETCH_FLESHED_USER, G.user.session);
2348         req.send(true);
2349
2350         G.user = req.result();
2351
2352         if(!G.user || G.user.length == 0) { 
2353                 G.user = null; return false; 
2354                 cookieManager.write(COOKIE_SES,"");
2355         }
2356
2357         G.user.session = ses;
2358         G.user.fleshed = true;
2359
2360         setSessionCookie(ses);
2361         return G.user;
2362 }
2363
2364 function checkUserSkin(new_skin) {
2365
2366         return; /* XXX do some debugging with this... */
2367
2368         var user_skin = getSkin();
2369         var cur_skin = grabSkinFromURL();
2370
2371         if(new_skin) user_skin = new_skin;
2372
2373         if(!user_skin) {
2374
2375                 if(grabUser()) {
2376                         if(grabUserPrefs()) {
2377                                 user_skin = G.user.prefs["opac.skin"];
2378                                 cookieManager.write( COOKIE_SKIN, user_skin, '+1y' );
2379                         }
2380                 }
2381         }
2382
2383         if(!user_skin) return;
2384
2385         if( cur_skin != user_skin ) {
2386                 var url = buildOPACLink();
2387                 goTo(url.replace(cur_skin, user_skin));
2388         }
2389 }
2390
2391 function updateUserSetting(setting, value, user) {
2392         if(user == null) user = G.user;
2393         var a = {};
2394         a[setting] = value;
2395         var req = new Request( UPDATE_USER_PREFS, user.session, a );
2396         req.send(true);
2397         return req.result();
2398 }
2399
2400 function commitUserPrefs() {
2401         var req = new Request( 
2402                 UPDATE_USER_PREFS, G.user.session, null, G.user.prefs );
2403         req.send(true);
2404         return req.result();
2405 }
2406
2407 function grabSkinFromURL() {
2408         var path = findBasePath();
2409         path = path.replace("/xml/", "");
2410         var skin = "";
2411         for( var i = path.length - 1; i >= 0; i-- ) {
2412                 var ch = path.charAt(i);
2413                 if(ch == "/") break;
2414                 skin += ch;
2415         }
2416
2417         var skin2 = "";
2418         for( i = skin.length - 1; i >= 0; i--)
2419                 skin2 += skin.charAt(i);
2420
2421         return skin2;
2422 }
2423
2424
2425 /* returns a fleshed G.user on success, false on failure */
2426 function doLogin(suppressEvents) {
2427
2428         abortAllRequests();
2429
2430         var uname = G.ui.login.username.value;
2431         var passwd = G.ui.login.password.value; 
2432
2433         var init_request = new Request( LOGIN_INIT, uname );
2434    init_request.send(true);
2435    var seed = init_request.result();
2436
2437    if( ! seed || seed == '0') {
2438       alert( "Error Communicating with Authentication Server" );
2439       return null;
2440    }
2441
2442         var args = {
2443                 password : hex_md5(seed + hex_md5(passwd)), 
2444                 type            : "opac", 
2445                 org             : getOrigLocation()
2446         };
2447
2448     r = fetchOrgSettingDefault(globalOrgTree.id(), 'opac.barcode_regex');
2449     if(r) REGEX_BARCODE = new RegExp(r);
2450     
2451     if( uname.match(REGEX_BARCODE) ) args.barcode = uname;
2452         else args.username = uname;
2453
2454    var auth_request = new Request( LOGIN_COMPLETE, args );
2455
2456         auth_request.request.alertEvent = false;
2457    auth_request.send(true);
2458    var auth_result = auth_request.result();
2459
2460         if(!auth_result) {
2461                 alertId('patron_login_failed');
2462                 return null;
2463         }
2464
2465         if( checkILSEvent(auth_result) ) {
2466
2467                 if( auth_result.textcode == 'PATRON_INACTIVE' ) {
2468                         alertId('patron_inactive_alert');
2469                         return;
2470                 }
2471
2472                 if( auth_result.textcode == 'PATRON_CARD_INACTIVE' ) {
2473                         alertId('patron_card_inactive_alert');
2474                         return;
2475                 }
2476
2477                 if( auth_result.textcode == 'LOGIN_FAILED' || 
2478                                 auth_result.textcode == 'PERM_FAILURE' ) {
2479                         alertId('patron_login_failed');
2480                         return;
2481                 }
2482         }
2483
2484
2485         AUTHTIME = parseInt(auth_result.payload.authtime);
2486         var u = grabUser(auth_result.payload.authtoken, true);
2487         if(u && ! suppressEvents) 
2488                 runEvt( "common", "locationChanged", u.ws_ou(), findOrgDepth(u.ws_ou()) );
2489
2490         checkUserSkin();
2491
2492         return u;
2493 }
2494
2495 function doLogout() {
2496
2497         /* cancel everything else */
2498         abortAllRequests();
2499
2500         /* be nice and delete the session from the server */
2501         if(G.user && G.user.session) { 
2502                 var req = new Request(LOGIN_DELETE, G.user.session);
2503       req.send(true);
2504                 try { req.result(); } catch(E){}
2505     }
2506
2507         G.user = null;
2508
2509         /* remove any cached data */
2510     dojo.require('dojo.cookie');
2511     dojo.cookie(COOKIE_SES, '', {expires:-1});
2512     dojo.cookie(COOKIE_RIDS, '', {expires:-1});
2513     dojo.cookie(COOKIE_SKIN, '', {expires:-1});
2514     dojo.cookie(COOKIE_SEARCH, '', {expires:-1});
2515
2516
2517         checkUserSkin("default");
2518         COUNT = 10;
2519
2520
2521         var args = {};
2522         args[PARAM_TERM] = "";
2523         args[PARAM_LOCATION] = getOrigLocation();
2524         args[PARAM_DEPTH] = findOrgDepth(getOrigLocation());
2525         args.page = "home";
2526
2527         
2528         var nored = false;
2529         try{ if(isFrontPage) nored = true; } catch(e){nored = false;}
2530         if(!nored) goTo(buildOPACLink(args, false, false));
2531 }
2532
2533
2534 function hideMe(obj) { addCSSClass(obj, config.css.hide_me); } 
2535 function unHideMe(obj) { removeCSSClass(obj, config.css.hide_me); }
2536
2537
2538 /* ----------------------------------------------------------------------- */
2539 /* build the org tree */
2540 /* ----------------------------------------------------------------------- */
2541 function drawOrgTree() {
2542         //setTimeout( 'buildOrgSelector(G.ui.common.org_tree, orgTreeSelector);', 10 );
2543         setTimeout( 'buildOrgSelector(G.ui.common.org_tree, orgTreeSelector);', 1 );
2544 }
2545         
2546 function checkOrgHiding() {
2547         var context_org = getOrigLocation() || globalOrgTree.id();
2548         var depth = fetchOrgSettingDefault( context_org, 'opac.org_unit_hiding.depth');
2549         if (isXUL()) {
2550                 return false; // disable org hiding for staff client
2551         }
2552         if ( findOrgDepth( context_org ) < depth ) {
2553                 return false; // disable org hiding if Original Location doesn't make sense with setting depth (avoids disjointed org selectors)
2554         }
2555         return { 'org' : findOrgUnit(context_org), 'depth' : depth };
2556 }
2557
2558 var orgTreeSelector;
2559 function buildOrgSelector(node) {
2560         var tree = new SlimTree(node,'orgTreeSelector');
2561         orgTreeSelector = tree;
2562         var orgHiding = checkOrgHiding();
2563         for( var i in orgArraySearcher ) { 
2564                 var node = orgArraySearcher[i];
2565                 if( node == null ) continue;
2566                 if(!isXUL() && !isTrue(node.opac_visible())) continue; 
2567                 if (orgHiding) {
2568                         if ( ! orgIsMine( orgHiding.org, node, orgHiding.depth ) ) {
2569                                 continue;
2570                         }
2571                 }
2572                 if(node.parent_ou() == null) {
2573                         tree.addNode(node.id(), -1, node.name(), 
2574                                 "javascript:orgSelect(" + node.id() + ");", node.name());
2575                 } else {
2576                         if (orgHiding && orgHiding.depth == findOrgDepth(node)) {
2577                                 tree.addNode(node.id(), -1, node.name(), 
2578                                         "javascript:orgSelect(" + node.id() + ");", node.name());
2579                         } else {
2580                                 tree.addNode(node.id(), node.parent_ou(), node.name(), 
2581                                         "javascript:orgSelect(" + node.id() + ");", node.name());
2582                         }
2583                 }
2584         }
2585         hideMe($('org_loading_div'));
2586         unHideMe($('org_selector_tip'));
2587         return tree;
2588 }
2589
2590 function orgSelect(id) {
2591         showCanvas();
2592         runEvt("common", "locationChanged", id, findOrgDepth(id) );
2593
2594
2595         var loc = findOrgLasso(getLasso());
2596         if (!loc) loc = findOrgUnit(id);
2597
2598         removeChildren(G.ui.common.now_searching);
2599         G.ui.common.now_searching.appendChild(text(loc.name()));
2600 }
2601
2602 function setFontSize(size) {
2603         scaleFonts(size);
2604         cookieManager.write(COOKIE_FONT, size, '+1y');
2605 }
2606
2607 var resourceFormats = [
2608    "text",
2609    "moving image",
2610    "sound recording", "software, multimedia",
2611    "still image",
2612    "cartographic",
2613    "mixed material",
2614    "notated music",
2615    "three dimensional object" ];
2616
2617
2618 function modsFormatToMARC(format) {
2619    switch(format) {
2620       case "text":
2621          return "at";
2622       case "moving image":
2623          return "g";
2624       case "sound recording":
2625          return "ij";
2626       case "sound recording-nonmusical":
2627          return "i";
2628       case "sound recording-musical":
2629          return "j";
2630       case "software, multimedia":
2631          return "m";
2632       case "still image":
2633          return "k";
2634       case "cartographic":
2635          return "ef";
2636       case "mixed material":
2637          return "op";
2638       case "notated music":
2639          return "cd";
2640       case "three dimensional object":
2641          return "r";
2642    }
2643    return "at";
2644 }
2645
2646
2647 function MARCFormatToMods(format) {
2648    switch(format) {
2649       case "a":
2650       case "t":
2651          return "text";
2652       case "g":
2653          return "moving image";
2654       case "i":
2655          return "sound recording-nonmusical";
2656       case "j":
2657          return "sound recording-musical";
2658       case "m":
2659          return "software, multimedia";
2660       case "k":
2661          return "still image";
2662       case "e":
2663       case "f":
2664          return "cartographic";
2665       case "o":
2666       case "p":
2667          return "mixed material";
2668       case "c":
2669       case "d":
2670          return "notated music";
2671       case "r":
2672          return "three dimensional object";
2673    }
2674    return "text";
2675 }
2676
2677 function MARCTypeToFriendly(format) {
2678         var words = $('format_words');
2679         switch(format) {
2680                 case 'a' :
2681                 case 't' : return $n(words, 'at').innerHTML;
2682                 default:
2683                         var node = $n(words,format);
2684                         if( node ) return node.innerHTML;
2685         }
2686         return "";
2687 }
2688
2689 function setResourcePic( img, resource ) {
2690         img.setAttribute( "src", "../../../../images/tor/" + resource + ".jpg");
2691         img.title = resource;
2692 }
2693
2694
2695
2696 function msg( text ) {
2697         try { alert( text ); } catch(e) {}
2698 }
2699
2700 function findRecord(id,type) {
2701         try {
2702                 for( var i = 0; i != recordsCache.length; i++ ) {
2703                         var rec = recordsCache[i];
2704                         if( rec && rec.doc_id() == id ) return rec;
2705                 }
2706         } catch(E){}
2707         var meth = FETCH_RMODS
2708         if(type == 'M') meth = FETCH_MRMODS;
2709         var req = new Request(meth, id);
2710         req.request.alertEvent = false;
2711         req.send(true);
2712         var res = req.result();
2713         if( checkILSEvent(res) ) return null; 
2714         return res;
2715 }
2716
2717 function Timer(name, node){
2718         this.name = name;
2719         this.count = 1;
2720         this.node = node;
2721 }
2722 Timer.prototype.start = 
2723         function(){_timerRun(this.name);}
2724 Timer.prototype.stop = 
2725         function(){this.done = true;}
2726 function _timerRun(tname) {
2727         var _t;
2728         eval('_t='+tname);
2729         if(_t.done) return;
2730         if(_t.count > 100) return;
2731         var str = ' . ';
2732         if( (_t.count % 5) == 0 ) 
2733                 str = _t.count / 5;
2734         _t.node.appendChild(text(str));
2735         setTimeout("_timerRun('"+tname+"');", 200);
2736         _t.count++;
2737 }
2738
2739 function checkILSEvent(obj) {
2740         if( obj && obj.ilsevent != null && obj.ilsevent != 0 )
2741                 return parseInt(obj.ilsevent);
2742         return null;
2743 }
2744
2745
2746 function alertILSEvent(evt, msg) {
2747    if(!msg) msg = "";
2748         if(msg)
2749                 alert(msg +'\n' + evt.textcode + '\n' + evt.desc );
2750         else 
2751                 alert(evt.textcode + '\n' + evt.desc );
2752 }
2753
2754
2755 var __authTimer;
2756 function AuthTimer(time) { 
2757         this.time = (time - LOGOUT_WARNING_TIME) * 1000; 
2758         if(__authTimer) 
2759                 try {clearTimeout(__authTimer.id)} catch(e){}
2760         __authTimer = this;
2761 }
2762
2763 AuthTimer.prototype.run = function() {
2764         this.id = setTimeout('_authTimerAlert()', this.time);
2765 }
2766
2767 function _authTimerAlert() {
2768         alert( $('auth_session_expiring').innerHTML );
2769         if(!grabUser(null, true)) doLogout();
2770 }
2771
2772
2773 function grabUserByBarcode( authtoken, barcode ) {
2774         var req = new Request( FETCH_USER_BYBARCODE, authtoken, barcode );
2775         req.send(true);
2776         return req.result();
2777 }
2778
2779
2780 function goHome() {
2781         goTo(buildOPACLink({page:HOME}));
2782 }
2783
2784
2785 function buildOrgSel(selector, org, offset, namecol) {
2786     if(!namecol) namecol = 'name';
2787     if(!isXUL() && !isTrue(org.opac_visible())) return;
2788         insertSelectorVal( selector, -1, 
2789                 org[namecol](), org.id(), null, findOrgDepth(org) - offset );
2790     var kids = org.children();
2791     if (kids) {
2792             for( var c = 0; c < kids.length; c++ )
2793                     buildOrgSel( selector, kids[c], offset, namecol);
2794     }
2795 }
2796
2797 function buildMergedOrgSel(selector, org_list, offset, namecol) {
2798     if(!namecol) namecol = 'name';
2799     for(var i = 0; i < org_list.length; i++) {
2800         var org = findOrgUnit(org_list[i]);
2801         insertSelectorVal( selector, -1, 
2802                     org[namecol](), org.id(), null, findOrgDepth(org) - offset );
2803         var kids = org.children();
2804         if (kids) {
2805                 for( var c = 0; c < kids.length; c++ )
2806                         buildOrgSel( selector, kids[c], offset, namecol);
2807         }
2808     }
2809 }
2810
2811
2812 function parseForm(form) {
2813         if(!form) return {};
2814
2815         var it = form.replace(/-\w+$/,"");
2816         var itf = null;
2817         var item_form;
2818         var item_type;
2819
2820         if(form.match(/-/)) itf = form.replace(/^\w+-/,"");
2821
2822         if(it) {
2823                 item_type = [];
2824                 for( var i = 0; i < it.length; i++ ) 
2825                         item_type.push( it.charAt(i) );
2826         }
2827
2828         if(itf) {
2829                 item_form = [];
2830                 for( var i = 0; i < itf.length; i++ ) 
2831                         item_form.push( itf.charAt(i) );
2832         }
2833
2834         return {item_type: item_type, item_form:item_form};
2835 }
2836
2837
2838 function isTrue(x) { return ( x && x != "0" && !(x+'').match(/^f$/i) ); }
2839
2840 function fetchPermOrgs() {
2841         var a = []; /* Q: why does arguments come accross as an object and not an array? A: because arguments is a special object, a collection */
2842
2843         for( var i = 0; i < arguments.length; i++ ) 
2844                 a.push(arguments[i])
2845
2846         var preq = new Request(FETCH_HIGHEST_PERM_ORG, 
2847                 G.user.session, G.user.id(), a );
2848         preq.send(true);
2849         return preq.result();
2850 }
2851
2852
2853 function print_tabs(t) {
2854         var r = '';
2855         for (var j = 0; j < t; j++ ) { r = r + "  "; }
2856         return r;
2857 }
2858 function formatJSON(s) {
2859         var r = ''; var t = 0;
2860         for (var i in s) {
2861                 if (s[i] == '{' || s[i] == '[' ) {
2862                         r = r + s[i] + "\n" + print_tabs(++t);
2863                 } else if (s[i] == '}' || s[i] == ']') {
2864                         t--; r = r + "\n" + print_tabs(t) + s[i];
2865                 } else if (s[i] == ',') {
2866                         r = r + s[i] + "\n" + print_tabs(t);
2867                 } else {
2868                         r = r + s[i];
2869                 }
2870         }
2871         return r;
2872 }
2873 /* ------------------------------------------------------------------------------------------------------ */
2874 /* org tree utilities */
2875 /* ------------------------------------------------------------------------------------------------------ */
2876
2877 try { console.log('org utils'); } catch(E) {}
2878
2879 function fetchOrgSettingDefault(orgId, name) {
2880     var req = new Request(FETCH_ORG_SETTING, orgId, name);
2881     req.send(true);
2882     var res = req.result();
2883     return (res) ? res.value : null;
2884 }
2885
2886 function fetchBatchOrgSetting(orgId, nameList, onload) {
2887     var req = new Request(
2888         'open-ils.actor:open-ils.actor.ou_setting.ancestor_default.batch', orgId, nameList);
2889     if(onload) {
2890         req.callback(function(r) { onload(r.getResultObject()); });
2891         req.send();
2892     } else {
2893         req.send(true);
2894         return req.result();
2895     }
2896 }
2897
2898
2899 /* takes an org unit or id and return the numeric depth */
2900 function findOrgDepth(org_id_or_node) {
2901         var org = findOrgUnit(org_id_or_node);
2902         if(!org) return -1;
2903         var type = findOrgType(org.ou_type());
2904         if(type) return type.depth();
2905         return -1;
2906 }
2907
2908 function findOrgTypeFromDepth(depth) {
2909         if( depth == null ) return null;
2910         for( var type = 0; type < globalOrgTypes.length; type++ ) {
2911                 var t = globalOrgTypes[type];
2912                 if( t.depth() == depth ) return t;
2913         }
2914 }
2915
2916 /* takes the org type id from orgunit.ou_type() field and returns
2917         the org type object */
2918 function findOrgType(type_id) {
2919         if(typeof type_id == 'object') return type_id;
2920         for(var type = 0; type < globalOrgTypes.length; type++) {
2921                 var t =globalOrgTypes[type]; 
2922                 if( t.id() == type_id || t.id() == parseInt(type_id) ) 
2923                         return t;
2924         }
2925         return null;
2926 }
2927
2928
2929 /* returns an org unit by id.  if an object is passed in as the id,
2930         then the object is assumed to be an org unit and is returned */
2931 function findOrgUnit(org_id) {
2932         return (typeof org_id == 'object') ? org_id : orgArraySearcher[org_id];
2933 }
2934
2935 function findOrgLasso(lasso_id) {
2936         if (typeof lasso_id == 'object') return lasso_id;
2937     for (var i = 0; i < _lasso.length; i++) {
2938         if (_lasso[i].id() == lasso_id) return _lasso[i];
2939     }
2940     return null;
2941 }
2942
2943 var orgArraySearcherSN = {};
2944 function findOrgUnitSN(shortname) {
2945         if (typeof shortname == 'object') return shortname;
2946         if( orgArraySearcherSN[shortname] ) return orgArraySearcherSN[shortname];
2947         _debug("fetching org by shortname "+shortname);
2948         var req = new Request(FETCH_ORG_BY_SHORTNAME, shortname);
2949         req.request.alertEvent = false;
2950         req.send(true);
2951         return req.result();
2952 }
2953
2954
2955 /* builds a trail from the top of the org tree to the node provide.
2956         basically fleshes out 'my orgs' 
2957         Returns an array of [org0, org1, ..., myorg] */
2958 function orgNodeTrail(node) {
2959         var na = new Array();
2960         while( node ) {
2961                 na.push(node);
2962                 node = findOrgUnit(node.parent_ou());
2963         }
2964         return na.reverse();
2965 }
2966
2967 function findSiblingOrgs(node) { return findOrgUnit(node.parent_ou()).children(); }
2968
2969 /* true if 'org' is 'me' or a child of mine, or optionally, a child of an ancestor org within the specified depth */
2970 function orgIsMine(me, org, depth) {
2971         if(!me || !org) {
2972                 return false;
2973         }
2974         if(me.id() == org.id()) {
2975                 return true;
2976         }
2977         if (depth) {
2978                 while (depth < findOrgDepth(me)) {
2979                         me = findOrgUnit( me.parent_ou() );
2980                 }
2981                 if(me.id() == org.id()) {
2982                         return true;
2983                 }
2984         }
2985         var kids = me.children();
2986         for( var i = 0; kids && i < kids.length; i++ ) {
2987                 if(orgIsMine(kids[i], org, false)) {
2988                         return true;
2989                 }
2990
2991         }
2992         return false;
2993 }
2994
2995 function orgIsMineFromSet(meList, org) {
2996     org = findOrgUnit(org);
2997     for(var i = 0; i < meList.length; i++) {
2998         if(orgIsMine(findOrgUnit(meList[i]), org))
2999             return true;
3000     }
3001     return false;
3002 }
3003
3004 var orgArraySearcher = {};
3005 var globalOrgTree;
3006 for (var i = 0; i < _l.length; i++) {
3007         var x = new aou();
3008         x.id(_l[i][0]);
3009         x.ou_type(_l[i][1]);
3010         x.parent_ou(_l[i][2]);
3011         x.name(_l[i][3]);
3012     x.opac_visible(_l[i][4]);
3013     x.shortname(_l[i][5]);
3014         orgArraySearcher[x.id()] = x;
3015 }
3016 for (var i in orgArraySearcher) {
3017         var x = orgArraySearcher[i];
3018         if (x.parent_ou() == null || x.parent_ou() == '') {
3019                 globalOrgTree = x;
3020                 continue;
3021         } 
3022
3023         var par = findOrgUnit(x.parent_ou());
3024         if (!par.children()) par.children(new Array());
3025         par.children().push(x);
3026 }
3027
3028 function _tree_killer () {
3029         for (var i in orgArraySearcher) {
3030                 x=orgArraySearcher[i];
3031                 x.children(null);
3032                 x.parent_ou(null);
3033                 orgArraySearcher[i]=null;
3034         }
3035         globalOrgTree = null;
3036         orgArraySearcher = null;
3037         globalOrgTypes = null;
3038 }
3039
3040 try { console.log('org utils end'); } catch(E) {}
3041
3042
3043 var XML_HTTP_GATEWAY = "osrf-gateway-v1";
3044 var XML_HTTP_SERVER = "";
3045
3046
3047 /* This object is thrown when network failures occur */
3048 function NetworkFailure(stat, url) { 
3049         this._status = stat; 
3050         this._url = url;
3051 }
3052
3053 NetworkFailure.prototype.status = function() { return this._status; }
3054 NetworkFailure.prototype.url = function() { return this._url; }
3055 NetworkFailure.prototype.toString = function() { 
3056         return "Network Failure: status = " + this.status() +'\n'+this.url(); 
3057 }
3058
3059
3060
3061 function isXUL() { try { if(IAMXUL) return true;}catch(e){return false;}; }
3062 var _allrequests = {};
3063
3064 // If the legacy JSON gateway is needed by the staff client, uncomment this
3065 /* 
3066 if(isXUL()) {
3067     XML_HTTP_GATEWAY = 'gateway';
3068 }
3069 */
3070
3071 function cleanRemoteRequests() {
3072         for( var i in _allrequests ) 
3073                 destroyRequest(_allrequests[i]);
3074 }
3075
3076 function abortAllRequests() {
3077         for( var i in _allrequests ) {
3078                 var r = _allrequests[i];
3079                 if(r) { 
3080                         r.abort();
3081                         destroyRequest(r);
3082                 }
3083         }
3084 }
3085
3086 function destroyRequest(r) {
3087         if(r == null) return;
3088
3089         if( r.xmlhttp ) {
3090                 r.xmlhttp.onreadystatechange = function(){};
3091                 r.xmlhttp = null;
3092         }
3093
3094         r.callback                              = null;
3095         r.userdata                              = null;
3096         _allrequests[r.id]      = null;
3097 }
3098
3099 /* ----------------------------------------------------------------------- */
3100 /* Request object */
3101 var rrId = 0;
3102 function RemoteRequest( service, method ) {
3103
3104         /* dojo is currently only available in the OPAC */
3105         try {
3106                 /* We want OpenSRF.locale for xx-YY format */
3107                 this.locale     = OpenSRF.locale;
3108         }
3109         catch (e) {
3110                 this.locale = null;
3111         }
3112         this.service    = service;
3113         this.method             = method;
3114         this.xmlhttp    = false;
3115         this.name               = null;
3116         this.alertEvent = true; /* only used when isXUL is false */
3117
3118         this.type               = "POST"; /* default */
3119         this.id                 = rrId++;
3120         this.cancelled = false;
3121
3122         this.setSecure(false);
3123         if(isXUL()) this.setSecure(true);
3124
3125         _allrequests[this.id] = this;
3126
3127         var i = 2;
3128         this.params = ""; 
3129
3130         while(i < arguments.length) {
3131                 var object = js2JSON(arguments[i++]);
3132                 this.params += "&param=" + encodeURIComponent(object);
3133         }
3134
3135         if(!this.params) { this.params = ""; }
3136
3137         this.param_string = "service=" + service + "&method=" + method + this.params;
3138         if (this.locale != null) {
3139                 this.param_string = this.param_string + "&locale=" + this.locale;
3140         }
3141         if( this.buildXMLRequest() == null ) alert("Browser is not supported!");
3142
3143 }
3144
3145
3146 RemoteRequest.prototype.timeout = function(t) {
3147         t *= 1000
3148         var req = this;
3149         req.timeoutFunc = setTimeout(
3150                 function() {
3151                         if( req && req.xmlhttp ) {
3152                                 req.cancelled = true;
3153                                 req.abort();
3154                                 if( req.abtCallback ) {
3155                                         req.abtCallback(req);
3156                                 }
3157                         }
3158                 },
3159                 t
3160         );
3161 }
3162
3163 RemoteRequest.prototype.abortCallback = function(func) {
3164         this.abtCallback = func;
3165 }
3166
3167 RemoteRequest.prototype.event = function(evt) {
3168         if( arguments.length > 0 )
3169                 this.evt = evt;
3170         return this.evt;
3171 }
3172
3173 RemoteRequest.prototype.abort = function() {
3174         if( this.xmlhttp ) {
3175                 /* this has to come before abort() or IE will puke on you */
3176                 this.xmlhttp.onreadystatechange = function(){};
3177                 this.xmlhttp.abort();
3178         }
3179 }
3180
3181 /* constructs our XMLHTTPRequest object */
3182 RemoteRequest.prototype.buildXMLRequest = function() {
3183         this.xmlhttp = buildXMLRequest();
3184         return true;
3185 }
3186
3187
3188 function buildXMLRequest() {
3189     try {
3190             return new XMLHttpRequest();
3191     } catch(e) {
3192             try { 
3193                     return new ActiveXObject("Msxml2.XMLHTTP"); 
3194             } catch (e2) {
3195                     try { 
3196                             return new ActiveXObject("Microsoft.XMLHTTP"); 
3197                     } catch (e3) {
3198                         alert("NEEDS NEWER JAVASCRIPT for XMLHTTPRequest()");
3199                 return null;
3200                     }
3201             }
3202     }
3203 }
3204
3205
3206 function _remoteRequestCallback(id) {
3207
3208         var object = _allrequests[id];
3209         if(object.cancelled) return;
3210
3211         if( object.xmlhttp.readyState == 4 ) {
3212
3213         try {
3214             object.duration = new Date().getTime() - object.sendTime;
3215             dump('request ' + object.id + ': duration = ' + object.duration + ' ms\n');
3216         } catch(ee){}
3217
3218                 try {
3219                         object.callback(object);
3220                 } catch(E) {
3221             throw E
3222                 } finally { 
3223                         destroyRequest(object); 
3224                         object = null; 
3225                 }  
3226         }
3227 }
3228
3229
3230 /* define the callback we use when this request has received
3231         all of its data */
3232 RemoteRequest.prototype.setCompleteCallback = function(callback) {
3233         if(this.cancelled) return;
3234         this.callback = callback;
3235         var id = this.id;
3236         this.xmlhttp.onreadystatechange = function() { _remoteRequestCallback(id); }
3237 }
3238
3239
3240 /* http by default.  This makes it https. *ONLY works when
3241         embedded in a XUL app. */
3242 RemoteRequest.prototype.setSecure = function(bool) {
3243         this.secure = bool; 
3244 }
3245
3246 RemoteRequest.prototype.send = function(blocking) {
3247
3248         if(this.cancelled) return;
3249
3250         /* determine the xmlhttp server dynamically */
3251         var url = location.protocol + "//" + location.host + "/" + XML_HTTP_GATEWAY;
3252
3253         if(isXUL()) {
3254                 if( XML_HTTP_SERVER ) 
3255                         url = 'http://'+XML_HTTP_SERVER+'/'+XML_HTTP_GATEWAY;
3256
3257                 if( url.match(/^http:/) && 
3258                                 (this.secure || location.href.match(/^https:/)) ) {
3259                         netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
3260                         url = url.replace(/^http:/, 'https:');
3261                 }
3262         }
3263
3264         var data = null;
3265         if( this.type == 'GET' ) url +=  "?" + this.param_string; 
3266
3267         this.url = url;
3268
3269    //if( isXUL() ) dump('request URL = ' + url + '?' + this.param_string + '\n');
3270
3271         try {
3272
3273                 if(blocking) this.xmlhttp.open(this.type, url, false);
3274                 else this.xmlhttp.open(this.type, url, true);
3275                 
3276         } catch(E) {
3277                 alert("Fatal error opening XMLHTTPRequest for URL:\n" + url + '\n' + E);
3278                 return;
3279         }
3280
3281         if( this.type == 'POST' ) {
3282                 data = this.param_string;
3283                 this.xmlhttp.setRequestHeader('Content-Type',
3284                                 'application/x-www-form-urlencoded');
3285         }
3286
3287         try {
3288                 var auth;
3289                 try { auth = cookieManager.read(COOKIE_SES) } catch(ee) {}
3290                 if( isXUL() ) auth = fetchXULStash().session.key;
3291                 if( auth ) 
3292                         this.xmlhttp.setRequestHeader('X-OILS-Authtoken', auth);
3293
3294         } catch(e) {}
3295
3296         if(data && data.match(/param=undefined/)) {
3297                 /* we get a bogus param .. replace with NULL */
3298                 try{dump('!+! UNDEFINED PARAM IN QUERY: ' + this.service + ' : ' + this.method+'\n');}catch(r){}
3299                 data = data.replace(/param=undefined/g,'param=null');
3300         }
3301
3302
3303     this.sendTime = new Date().getTime();
3304         try{ this.xmlhttp.send( data ); } catch(e){}
3305
3306         return this;
3307 }
3308
3309 /* returns the actual response text from the request */
3310 RemoteRequest.prototype.getText = function() {
3311         return this.xmlhttp.responseText;
3312 }
3313
3314 RemoteRequest.prototype.isReady = function() {
3315         return this.xmlhttp.readyState == 4;
3316 }
3317
3318
3319 /* returns the JSON->js result object  */
3320 RemoteRequest.prototype.getResultObject = function() {
3321
3322         if(this.cancelled) return null;
3323         if(!this.xmlhttp) return null;
3324
3325         var failed = false;
3326         var status = null;
3327         this.event(null);
3328
3329         /*
3330         try {
3331                 dump(this.url + '?' + this.param_string + '\n' +
3332                         'Returned with \n\tstatus = ' + this.xmlhttp.status + 
3333                         '\n\tpayload= ' + this.xmlhttp.responseText + '\n');
3334         } catch(e) {}
3335         */
3336
3337         try {
3338                 status = this.xmlhttp.status;
3339                 if( status != 200 ) failed = true;
3340         } catch(e) { failed = true; }
3341
3342         if( failed ) {
3343                 if(!status) status = '<unknown>';
3344                 try{dump('! NETWORK FAILURE.  HTTP STATUS = ' +status+'\n'+this.param_string+'\n');}catch(e){}
3345                 if(isXUL()) 
3346                         throw new NetworkFailure(status, this.param_string);
3347                 else return null;
3348         }
3349
3350         var text = this.xmlhttp.responseText;
3351
3352         //try{if(getDebug()) _debug('response: ' + text + '\n')}catch(e){}
3353
3354         if(text == "" || text == " " || text == null) {
3355                 try { dump('dbg: Request returned no text!\n'); } catch(E) {}
3356                 if(isXUL()) 
3357                         throw new NetworkFailure(status, this.param_string);
3358                 return null;
3359         }
3360
3361         var obj = JSON2js(text);
3362         if(!obj) return null;
3363
3364         if( obj.status != 200 ) {
3365
3366                 var str = 'A server error occurred. Debug information follows: ' +
3367                                         '\ncode = '             + obj.status + 
3368                                         '\ndebug: '             + obj.debug + 
3369                                         '\npayload: '   + js2JSON(obj.payload); 
3370
3371                 if(isXUL()) {
3372                         dump(str);
3373                         throw obj;
3374
3375                 } else {
3376                         _debug(str);
3377                         throw str;
3378                 }
3379         }
3380
3381         var payload = obj.payload;
3382         if(!payload || payload.length == 0) return null;
3383         payload = (payload.length == 1) ? payload[0] : payload;
3384
3385         if(!isXUL()) {
3386                 if( checkILSEvent(payload) ) {
3387                         this.event(payload);
3388                         if( this.alertEvent ) {
3389                                 alertILSEvent(payload);
3390                                 return null;
3391                         }
3392                 } 
3393         }
3394
3395         return payload;
3396 }
3397
3398 /* adds a new parameter to the request */
3399 RemoteRequest.prototype.addParam = function(param) {
3400         var string = encodeURIComponent(js2JSON(param));
3401         this.param_string += "&param=" + string;
3402 }
3403
3404 function fetchXULStash() {
3405         if( isXUL() ) {
3406                 try {
3407                         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
3408                         var __OILS = new Components.Constructor("@mozilla.org/openils_data_cache;1", "nsIOpenILS");
3409                         var data_cache = new __OILS( );
3410                         return data_cache.wrappedJSObject.OpenILS.prototype.data;
3411         
3412                 } catch(E) {
3413                         _debug('Error in OpenILS.data._debug_stash(): ' + js2JSON(E) );
3414                 }
3415         }
3416         return {};
3417 }
3418
3419
3420
3421 /* these events should be used by all */
3422
3423 window.onunload = windowUnload;
3424
3425 attachEvt("common", "init", loadUIObjects);
3426 //attachEvt("common", "init", initParams);
3427 attachEvt("common", "init", initCookies);
3428
3429 attachEvt("common", "unload", _tree_killer);
3430 try{ attachEvt("common", "unload", cleanRemoteRequests);} catch(e){}
3431
3432 function init() {
3433
3434         initParams();
3435
3436         if( getLocation() == null && getOrigLocation() != null )
3437                 LOCATION = getOrigLocation();
3438
3439         if( getLocation() == null && getOrigLocation() == null )
3440                 LOCATION = globalOrgTree.id();
3441
3442         /* if they click on the home page and the origlocation is set
3443                 take the opac back to the origlocation */
3444         if( findCurrentPage() == HOME && getOrigLocation() != null )
3445                 LOCATION = getOrigLocation();
3446
3447         if(getDepth() == null) DEPTH = findOrgDepth(getLocation());
3448
3449
3450         runEvt('common','init');
3451
3452         var cgi = new CGI();
3453         if( grabUser() ) {
3454                 if( cgi.param(PARAM_LOCATION) == null ) {
3455                         var org = G.user.prefs[PREF_DEF_LOCATION];
3456                         var depth = G.user.prefs[PREF_DEF_DEPTH];
3457
3458                         if(org == null) org = G.user.ws_ou();
3459                         if(depth == null) depth = findOrgDepth(org);
3460
3461                         LOCATION = org;
3462                         DEPTH = depth;
3463                 }
3464         }
3465
3466         runEvt("common", "run");
3467         //checkUserSkin();
3468
3469         var loc = findOrgLasso(getLasso());
3470         if (!loc) loc = findOrgUnit(getLocation());
3471
3472         if (getLasso()) G.ui.common.now_searching.appendChild(text('Search group: '));
3473         G.ui.common.now_searching.appendChild(text(loc.name()));
3474 }
3475
3476 function windowUnload() { runEvt("common", "unload"); }
3477 /**
3478 * This function should return a URL which points to the book cover image based on ISBN.
3479 */
3480
3481
3482 function buildISBNSrc(isbn, size) {
3483         size = (size) ? size : 'small';
3484         var protocol = (OILS_OPAC_STATIC_PROTOCOL) ? OILS_OPAC_STATIC_PROTOCOL + ':' : location.protocol;
3485     if(OILS_OPAC_IMAGES_HOST)
3486         return protocol + '//' + OILS_OPAC_IMAGES_HOST + size + '/' + isbn;
3487         return '../../../../extras/ac/jacket/'+size+'/'+isbn;
3488 }      
3489
3490
3491 function acMakeURL(type, key) {
3492         return '../../../../extras/ac/' + type + '/html/' + key;
3493 }
3494
3495
3496 function acCollectData( key, callback ) {
3497         var context = { key : key, callback: callback, data : {} };
3498         acCollectItem(context, 'summary');
3499         acCollectItem(context, 'reviews');
3500         acCollectItem(context, 'toc');
3501         acCollectItem(context, 'excerpt');
3502         acCollectItem(context, 'anotes');
3503 }
3504
3505 function acCheckDone(context) {
3506         if(     context.data.reviews && context.data.reviews.done &&
3507                         context.data.toc                && context.data.toc.done &&
3508                         context.data.excerpt && context.data.excerpt.done &&
3509                         context.data.anotes     && context.data.anotes.done ) {
3510
3511                 if(context.callback) context.callback(context.data);
3512         }
3513 }
3514
3515 function acCollectItem(context, type) {
3516         var req = buildXMLRequest();
3517         req.open('GET', acMakeURL(type, context.key), true);
3518         req.onreadystatechange = function() {
3519                 if( req.readyState == 4 ) {
3520                         context.data[type] = { done : true }
3521
3522                         if(IE) {
3523
3524                                 /* Someone please explain why IE treats status 404 as status 200?? 
3525                                         On second thought, don't bother.
3526                                 */
3527                                 if( ! req.responseText.match(
3528                                         /The requested URL.*was not found on this server/) )
3529                                         context.data[type].html = req.responseText;
3530
3531                         } else {
3532                                 if( req.status != 404 ) 
3533                                         context.data[type].html = req.responseText;
3534                         }
3535                         acCheckDone(context);
3536                 }
3537         }
3538         req.send(null);
3539 }
3540
3541