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