Disabling browser address field surveillance.
[pazpar2-moved-to-github.git] / www / demo / search.js
1 /* $Id: search.js,v 1.44 2007-01-20 20:11:36 sondberg Exp $
2  * ---------------------------------------------------
3  * Javascript container
4  */
5
6 var xmlHttp
7 var xinitSession;
8 var xloadTargets;
9 var xsearch;
10 var xshow;
11 var xstat;
12 var xtermlist;
13 var xfetchDetails;
14 var session = false;
15 var targetsloaded = false;
16 var shown;
17 var searchtimer;
18 var showtimer;
19 var termtimer;
20 var stattimer;
21 var session_cells = Array('query', 'startrec', 'action_type');
22 var old_session = session_read();
23 var url_surveillence;
24 var recstoshow = 20;
25 var page_window = 5;  // Number of pages prior to and after the current page
26 var facet_list;
27 var cur_sort = "relevance";
28 var searched = 0;
29 var cur_id = -1;
30 var cur_rec = 0;
31
32 function initialize ()
33 {
34     facet_list = get_available_facets();
35     start_session();
36     set_sort();
37 }
38
39 function GetXmlHttpObject()
40
41     var objXMLHttp=null
42     if (window.XMLHttpRequest)
43       {
44       objXMLHttp=new XMLHttpRequest()
45       }
46     else if (window.ActiveXObject)
47       {
48       objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP")
49       }
50     return objXMLHttp
51
52
53 function SendXmlHttpObject(obj, url, handler)
54 {
55     obj.onreadystatechange=handler;
56     obj.open("GET", url);
57     obj.send(null);
58 }
59
60 function session_started()
61 {
62     if (xinitSession.readyState != 4)
63         return;
64     var xml = xinitSession.responseXML;
65     var sesid = xml.getElementsByTagName("session")[0].childNodes[0].nodeValue;
66     assign_text(document.getElementById("status"), 'Live');
67     session = sesid;
68     setTimeout(ping_session, 50000);
69 }
70
71 function start_session()
72 {
73     xinitSession = GetXmlHttpObject();
74     var url="search.pz2?";
75     url += "command=init";
76     xinitSession.onreadystatechange=session_started;
77     xinitSession.open("GET", url);
78     xinitSession.send(null);
79 }
80
81 function ping_session()
82 {
83     if (!session)
84         return;
85     var url = "search.pz2?command=ping&session=" + session;
86     SendXmlHttpObject(xpingSession = GetXmlHttpObject(), url, session_pinged);
87 }
88
89 function session_pinged()
90 {
91     if (xpingSession.readyState != 4)
92         return;
93     var xml = xpingSession.responseXML;
94     var error = xml.getElementsByTagName("error");
95     if (error[0])
96         location = "?";
97     else
98         setTimeout(ping_session, 50000);
99 }
100
101 function update_action (new_action) {
102     document.search.action_type.value = new_action;
103 }
104
105
106 function make_pager (hits, offset, max) {
107     var html = '';
108     var off;
109     var start_offset = offset - page_window * max;
110     var div_elem = document.createElement('div');
111     
112     div_elem.className = 'pages';
113
114     if (start_offset < 0) {
115         start_offset = 0;
116     }
117
118     for (off = start_offset;
119          off < hits && off < (start_offset + 2 * page_window * max); 
120          off += max) {
121         
122         var p = off / max + 1;
123         var page_elem = create_element('a', p);
124         var newline_node = document.createTextNode(' ');
125
126         if ((offset >= off) && (offset < (off + max))) {
127             page_elem.className = 'select';
128         }
129
130         page_elem.setAttribute('off', off);
131         page_elem.style.cursor = 'pointer';
132         page_elem.onclick = function () {
133             update_offset(this.getAttribute('off'));
134         };
135
136         div_elem.appendChild(page_elem);
137         div_elem.appendChild(newline_node);
138     }
139
140     return div_elem;
141 }
142
143
144 function update_offset (offset) {
145     clearTimeout(searchtimer);
146     document.search.startrec.value = offset;
147     update_action('page');
148     check_search();
149     update_history();
150     return false;
151 }
152
153
154 function create_element (name, cdata) {
155     var elem_node = document.createElement(name);
156     var text_node = document.createTextNode(cdata);
157     elem_node.appendChild(text_node);
158
159     return elem_node;
160 }
161
162
163 function clear_cell (cell) {
164     while (cell.hasChildNodes())
165         cell.removeChild(cell.firstChild);
166 }
167
168
169 function append_text(cell, text) {
170     text_node = document.createTextNode(text);
171     cell.appendChild(text_node);
172 }
173
174
175 function assign_text (cell, text) {
176     clear_cell(cell);
177     append_text(cell, text);
178 }
179
180 function set_sort_opt(n, opt, str)
181 {
182     var txt = document.createTextNode(str);
183     if (opt == cur_sort)
184         n.appendChild(txt);
185     else
186     {
187         var a = document.createElement('a');
188         a.appendChild(txt);
189         a.setAttribute('href', "");
190         a.setAttribute('onclick', "set_sort('" + opt + "'); return false");
191         n.appendChild(a);
192     }
193 }
194
195 function set_sort(sort)
196 {
197     if (sort && sort != cur_sort)
198     {
199         cur_sort = sort;
200         if (searched)
201             check_search();
202     }
203
204     var t = document.getElementById("sortselect");
205     clear_cell(t);
206     t.appendChild(document.createTextNode("Sort results by: "));
207     set_sort_opt(t, 'relevance', 'Relevance');
208     t.appendChild(document.createTextNode(" or "));
209     set_sort_opt(t, 'title:1', 'Title');
210 }
211
212 function displayname(name)
213 {
214     if (name == 'md-author')
215         return 'Author';
216     else if (name == 'md-subject')
217         return 'Subject';
218     else if (name == 'md-date')
219         return 'Date';
220     else if (name == 'md-isbn')
221         return 'ISBN';
222     else if (name == 'md-publisher')
223         return 'Publisher';
224     else if (name == 'md-url')
225         return 'URL';
226     else if (name == 'md-title')
227         return '@';
228     else if (name == 'md-id')
229         return 'Local ID';
230     else if (name == 'md-lccn')
231         return 'LCCN';
232     else if (name == 'recid')
233         return '@';
234     else if (name == 'location')
235         return '@';
236     else
237         return name;
238 }
239
240 function hyperlink_field(name)
241 {
242     if (name == 'md-author')
243         return 'au';
244     else if (name == 'md-subject')
245         return 'su';
246     else if (name == 'md-url')
247         return 'URL';
248     else
249         return 0;
250 }
251
252 function  paint_details_tr(name, dn)
253 {
254     //emit a table row
255     var dname = displayname(name);
256     var ln = create_element('b', dname);
257     var tln = document.createElement('td');
258     tln.setAttribute('width', 90);
259     tln.setAttribute('valign', 'top');
260     tln.appendChild(ln);
261     var tr = document.createElement('tr');
262     tr.appendChild(tln);
263     tr.appendChild(dn);
264     return tr;
265 }
266
267 function paint_data_elements(target, node)
268 {
269     var nodes = node.childNodes;
270     var dn = 0;
271     var lastname = '';
272     var i;
273     for (i = 0; i < nodes.length; i++)
274     {
275         if (nodes[i].nodeType != 1)
276             continue;
277         var name = nodes[i].nodeName;
278         if (name == 'recid' || name == 'md-title')
279             continue;
280         if (name != lastname && lastname != 'location') 
281         {
282             if (dn)
283             {
284                 var tr = paint_details_tr(lastname, dn);
285                 target.appendChild(tr);
286             }
287             dn = document.createElement('td');
288             lastname = name;
289         }
290         if (name == 'location')
291         {
292             target.appendChild(paint_details_tr('Location', paint_subrecord(nodes[i])));
293             continue;
294         }
295         if (!nodes[i].childNodes[0])
296                 continue;
297         var value = nodes[i].childNodes[0].nodeValue;
298         if (dn.childNodes[0])
299             dn.appendChild(document.createTextNode('; '));
300         var hyl = hyperlink_field(name);
301         var nv;
302         if (hyl)
303         {
304             nv = create_element('a', value);
305             if (hyl == 'URL')
306             {
307                 nv.setAttribute('href', value);
308                 nv.setAttribute('target', '_blank');
309             }
310             else
311             {
312                 nv.setAttribute('href', '#');
313                 nv.setAttribute('term', value);
314                 nv.setAttribute('searchfield', hyl);
315                 nv.onclick = function() { hyperlink_search(this); return false; };
316             }
317         }
318         else if (name == 'md-lccn')
319         {
320             nv = document.createElement('span');
321             nv.appendChild(document.createTextNode(value + ' '));
322             var link = create_element('a', 'Show title in LoC');
323                 link.setAttribute('target', '_blank');
324                 link.setAttribute('href', 'http://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?DB=local&CNT=10&CMD=10+records+per+page&CMD=lccn+' + value);
325             nv.appendChild(link);
326
327         }
328         else
329             nv = document.createTextNode(value);
330         dn.appendChild(nv);
331     }
332     if (dn && lastname != 'location')
333     {
334         var tr = paint_details_tr(lastname, dn);
335         target.appendChild(tr);
336     }
337 }
338
339 function paint_subrecord(node)
340 {
341     var table = document.createElement('table');
342     var zurl = node.getAttribute('id');
343     var name = node.getAttribute('name');
344     var tr;
345     if (name)
346         tr = paint_details_tr('Source', document.createTextNode(name));
347     else
348         tr = paint_details_tr('Source', document.createTextNode(zurl));
349     table.appendChild(tr);
350     paint_data_elements(table, node);
351     return table;
352 }
353
354 function paint_details(body, xml)
355 {
356     clear_cell(body);
357     var table = document.createElement('table');
358     table.setAttribute('cellpadding', 2);
359     paint_data_elements(table, xml.childNodes[0]);
360     body.appendChild(table);
361     body.style.display = 'inline';
362 }
363
364 function show_details()
365 {
366     if (xfetchDetails.readyState != 4)
367         return;
368     var xml = xfetchDetails.responseXML;
369     var error = xml.getElementsByTagName("error");
370     if (error[0])
371     {
372         var msg = error[0].childNodes[0].nodeValue;
373         alert(msg);
374         location = "?";
375         return;
376     }
377
378     var idn = xml.getElementsByTagName('recid');
379     if (!idn[0])
380         return;
381     var id = idn[0].childNodes[0].nodeValue;
382     cur_id = id;
383     cur_rec = xml;
384
385     var nodes = document.getElementsByName('listrecord');
386     var i;
387     for (i = 0; i < nodes.length; i++)
388     {
389         var dets = nodes[i].getElementsByTagName('div');
390         if (dets[0])
391             dets[0].style.display = 'none';
392     }
393
394     var body = document.getElementById('rec_' + id);
395     if (!body)
396         return;
397     paint_details(body, xml);
398 }
399
400 function hyperlink_search(obj)
401 {
402     var field = obj.getAttribute('searchfield');
403     var term = obj.getAttribute('term');
404     var queryfield  = document.getElementById('query');
405     queryfield.value = field + '=' + term;
406     start_search();
407 }
408
409 function fetch_details(id)
410 {
411     cur_id = -1;
412     if (id == cur_id)
413     {
414         cur_id = -1;
415         return;
416     }
417     if (!session)
418         return;
419     var url = "search.pz2?session=" + session +
420         "&command=record" +
421         "&id=" + id;
422     SendXmlHttpObject(xfetchDetails = GetXmlHttpObject(), url, show_details);
423 }
424
425 function show_records()
426 {
427     if (xshow.readyState != 4)
428         return;
429     var i;
430     var xml = xshow.responseXML;
431     var body = document.getElementById("body");
432     var hits = xml.getElementsByTagName("hit");
433
434     clear_cell(body);
435
436     if (!hits[0]) // We should never get here with blocking operations
437     {
438         assign_text(body, 'No records yet');
439         searchtimer = setTimeout(check_search, 250);
440     }
441     else
442     {
443         var total = Number(xml.getElementsByTagName('total')[0].childNodes[0].nodeValue);
444         var merged = Number(xml.getElementsByTagName('merged')[0].childNodes[0].nodeValue);
445         var start = Number(xml.getElementsByTagName('start')[0].childNodes[0].nodeValue);
446         var num = Number(xml.getElementsByTagName('num')[0].childNodes[0].nodeValue);
447         var clients = Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
448         var pager = make_pager(merged, start,recstoshow);
449         var break_node1 = document.createElement('br');
450         var break_node2 = document.createElement('br');
451         var record_container = document.createElement('div');
452         var interval = create_element('div', 'Records : ' + (start + 1) +
453                                              ' to ' + (start + num) + ' of ' +
454                                              merged + ' (total hits: ' +
455                                              total + ')');
456         searched = 1;
457         interval.className = 'results';
458         record_container.className = 'records';
459
460         body.appendChild(pager);
461         body.appendChild(interval);
462         body.appendChild(break_node1);
463         body.appendChild(break_node2);
464         body.appendChild(record_container);
465
466         for (i = 0; i < hits.length; i++)
467         {
468             var tn = hits[i].getElementsByTagName("md-title");
469             var title = '';
470             var an = hits[i].getElementsByTagName("md-author");
471             var author = '';
472             var cn = hits[i].getElementsByTagName("count");
473             var count = 1;
474             var idn = hits[i].getElementsByTagName("recid");
475
476             if (tn[0]) {
477                 title = tn[0].childNodes[0].nodeValue;
478             } else {
479                 title = 'N/A';
480             }
481             if (an[0] && an[0].childNodes[0])
482                     author = an[0].childNodes[0].nodeValue;
483             if (cn[0])
484                 count = Number(cn[0].childNodes[0].nodeValue);
485             var id = idn[0].childNodes[0].nodeValue;
486             
487             var record_div = document.createElement('div');
488             record_div.className = 'record';
489             record_div.setAttribute('name', 'listrecord');
490
491             var record_cell = create_element('a', title);
492             record_cell.setAttribute('href', '#' + id);
493             record_cell.setAttribute('onclick', 'fetch_details(' + id + '); return false');
494             record_div.appendChild(record_cell);
495             if (author)
496             {
497                 record_div.appendChild(document.createTextNode(', by '));
498                 var al = create_element('a', author);
499                 al.setAttribute('href', '#');
500                 al.setAttribute('term', author);
501                 al.setAttribute('searchfield', 'au');
502                 al.onclick = function() { hyperlink_search(this); return false; };
503                 record_div.appendChild(al);
504             }
505             if (count > 1)
506                 record_div.appendChild(document.createTextNode(
507                         ' (' + count + ')'));
508             var det_div = document.createElement('div');
509             if (id == cur_id)
510                 paint_details(det_div, cur_rec);
511             else
512                 det_div.style.display = 'none';
513             det_div.setAttribute('id', 'rec_' + id);
514             det_div.setAttribute('name', 'details');
515             record_div.appendChild(det_div);
516             record_container.appendChild(record_div);
517         }
518
519         shown++;
520         if (clients > 0)
521         {
522             if (shown < 5)
523                 searchtimer = setTimeout(check_search, 1000);
524             else
525                 searchtimer = setTimeout(check_search, 2000);
526         }
527     }
528     if (!termtimer)
529         termtimer = setTimeout(check_termlist, 500);
530 }
531
532 function check_search()
533 {
534     clearTimeout(searchtimer);
535     var url = "search.pz2?" +
536         "command=show" +
537         "&start=" + document.search.startrec.value +
538         "&num=" + recstoshow +
539         "&session=" + session +
540         "&sort=" + cur_sort +
541         "&block=1";
542     xshow = GetXmlHttpObject();
543     xshow.onreadystatechange=show_records;
544     xshow.open("GET", url);
545     xshow.send(null);
546 }
547
548
549 function refine_query (obj) {
550     var term = obj.getAttribute('term');
551     var cur_termlist = obj.getAttribute('facet');
552     var query_cell = document.getElementById('query');
553     
554     term = term.replace(/[\(\)]/g, '');
555     
556     if (cur_termlist == 'subject')
557         query_cell.value += ' and su=' + term;
558     else if (cur_termlist == 'author')
559         query_cell.value += ' and au=' + term;
560     else if (cur_termlist == 'date')
561         query_cell.value += ' and date=' + term;
562
563     start_search();
564 }
565
566 function clear_termlists()
567 {
568     var i;
569     for (i = 0; i < facet_list.length; i++)
570         clear_cell(facet_list[i][1]);
571 }
572
573 function show_termlists()
574 {
575     if (xtermlist.readyState != 4)
576         return;
577
578     var i;
579     var xml = xtermlist.responseXML;
580     var clients =
581         Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
582     var lists = xml.getElementsByTagName("list");
583
584     for (i = 0; i < lists.length; i++)
585     {
586         var listname = lists[i].getAttribute('name');
587         var body = document.getElementById('facet_' + listname + '_terms');
588         if (body.style.display == 'none')
589             continue;
590         clear_cell(body);
591         var terms = lists[i].getElementsByTagName('term');
592         var t;
593         for (t = 0; t < terms.length; t++)
594         {
595             var namen = terms[t].getElementsByTagName("name");
596             var freqn = terms[t].getElementsByTagName("frequency");
597             if (namen[0])
598                 var term = namen[0].childNodes[0].nodeValue;
599                 var freq = freqn[0].childNodes[0].nodeValue;
600                 var refine_cell = create_element('a', term + ' (' + freq + ')');
601                 refine_cell.setAttribute('href', '#');
602                 refine_cell.setAttribute('term', term);
603                 refine_cell.setAttribute('facet', listname);
604                 refine_cell.onclick = function () {
605                     refine_query(this);
606                     return false;
607                 };
608                 body.appendChild(refine_cell);
609         }
610     }
611     if (clients > 0)
612         termtimer = setTimeout(check_termlist, 1000);
613 }
614
615 function check_termlist()
616 {
617     var facet_names = '';
618     var i;
619     for (i = 0; i < facet_list.length; i++)
620         if (facet_list[i][1].style.display != 'none')
621         {
622             if (facet_names)
623                 facet_names += ',';
624             facet_names += facet_list[i][0];
625         }
626     var url = "search.pz2?" +
627         "command=termlist" +
628         "&session=" + session +
629         "&name=" + facet_names +
630         "&num=12";
631     SendXmlHttpObject(xtermlist = GetXmlHttpObject(), url, show_termlists);
632 }
633
634 function show_stat()
635 {
636     if (xstat.readyState != 4)
637         return;
638     var i;
639     var xml = xstat.responseXML;
640     var body = document.getElementById("stat");
641     var nodes = xml.childNodes[0].childNodes;
642     var clients =
643         Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
644     if (!nodes[0])
645     {
646         stattimer  = setTimeout(check_stat, 500);
647     }
648     else
649     {
650         assign_text(body, '(');
651         for (i = 0; i < nodes.length; i++)
652         {
653             if (nodes[i].nodeType != 1)
654                 continue;
655             var value = nodes[i].childNodes[0].nodeValue;
656             if (value == 0)
657                 continue;
658             var name = nodes[i].nodeName;
659             append_text(body, ' ' + name + '=' + value);
660         }
661
662         append_text(body, ')');
663         if (clients > 0)
664             stattimer = setTimeout(check_stat, 2000);
665     }
666 }
667
668 function check_stat()
669 {
670     var url = "search.pz2?" +
671         "command=stat" +
672         "&session=" + session;
673     xstat = GetXmlHttpObject();
674     xstat.onreadystatechange=show_stat;
675     xstat.open("GET", url);
676     xstat.send(null);
677 }
678
679 function search_started()
680 {
681     if (xsearch.readyState != 4)
682         return;
683     var xml = xsearch.responseXML;
684     var error = xml.getElementsByTagName("error");
685     if (error[0])
686     {
687         var msg = error[0].childNodes[0].nodeValue;
688         alert(msg);
689         return;
690     }
691     check_search();
692     stattimer = setTimeout(check_stat, 1000);
693 }
694
695 function start_search()
696 {
697     clearTimeout(termtimer);
698     termtimer = 0;
699     clearTimeout(searchtimer);
700     searchtimer = 0;
701     clearTimeout(stattimer);
702     stattimer = 0;
703     clearTimeout(showtimer);
704     showtimer = 0;
705     cur_id = -1;
706     clear_termlists();
707     var query = escape(document.getElementById('query').value);
708     var url = "search.pz2?" +
709         "command=search" +
710         "&session=" + session +
711         "&query=" + query;
712     xsearch = GetXmlHttpObject();
713     xsearch.onreadystatechange=search_started;
714     xsearch.open("GET", url);
715     xsearch.send(null);
716     clear_cell(document.getElementById("body"));
717     update_history();
718     shown = 0;
719     document.search.startrec.value = 0;
720 }
721
722 function session_encode ()
723 {
724     var i;
725     var session = '';
726
727     for (i = 0; i < session_cells.length; i++)
728     {
729         var name = session_cells[i];
730         var value = escape(document.getElementById(name).value);
731         session += '&' + name + '=' + value;
732     }
733
734     return session;
735 }
736
737
738 function session_restore (session)
739 {
740     var fields = session.split(/&/);
741     var i;
742
743     for (i = 1; i < fields.length; i++)
744     {
745         var pair = fields[i].split(/=/);
746         var key = pair.shift();
747         var value = pair.join('=');
748         var cell = document.getElementById(key);
749
750         cell.value = value;
751     }
752     
753 }
754
755
756 function session_read ()
757 {
758     var ses = window.location.hash.replace(/^#/, '');
759     return ses;
760 }
761
762
763 function session_store (new_value)
764 {
765     window.location.hash = '#' + new_value;
766 }
767
768
769 function update_history ()
770 {
771     var session = session_encode();
772     session_store(session);
773     old_session = session;
774 }
775
776
777 function session_check ()
778 {
779     var session = session_read();
780     var action = document.search.action_type.value;
781
782     clearInterval(url_surveillence);
783
784     if ( session != unescape(old_session) )
785     {
786         session_restore(session);
787
788         if (action == 'search') {
789             start_search();
790         } else if (action == 'page') {
791             check_search();
792         } else {
793             alert('Unregocnized action_type: ' + action);
794             return;
795         }
796     }
797     
798     url_surveillence = setInterval(session_check, 200);
799 }
800
801
802 function get_available_facets () {
803     var facet_container = document.getElementById('termlists');
804     var facet_cells = facet_container.childNodes;
805     var facets = Array();
806     var i;
807
808     for (i = 0; i < facet_cells.length; i++) {
809         var cell = facet_cells.item(i);
810
811         if (cell.className == 'facet') {
812             var facet_name = cell.id.replace(/^facet_([^_]+)_terms$/, "$1");
813             facets.push(Array(facet_name, cell));
814         }
815     }
816
817     return facets;
818 }
819
820
821 function get_facet_container (obj) {
822     return document.getElementById(obj.id + '_terms');
823 }
824
825
826 function toggle_facet (obj) {
827     var container = get_facet_container(obj);
828
829     if (obj.className == 'selected') {
830         obj.className = 'unselected';
831         container.style.display = 'inline';
832         check_termlist();
833     } else {
834         obj.className = 'selected';
835         container.style.display = 'none';
836     }
837 }