1c521ac21c07b7e96ba74079375bee4d83c50814
[pazpar2-moved-to-github.git] / www / demo / search.js
1 /* $Id: search.js,v 1.47 2007-02-06 23:22:49 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     elem_node.innerHTML = cdata;
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('id', opt);
191         a.onclick = function() { set_sort(this.getAttribute('id')); 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-description')
232         return 'Description';
233     else if (name == 'md-lccn')
234         return 'LCCN';
235     else if (name == 'recid')
236         return '@';
237     else if (name == 'location')
238         return '@';
239     else
240         return name;
241 }
242
243 function hyperlink_field(name)
244 {
245     if (name == 'md-author')
246         return 'au';
247     else if (name == 'md-subject')
248         return 'su';
249     else if (name == 'md-url')
250         return 'URL';
251     else
252         return 0;
253 }
254
255 function  paint_details_tr(name, dn)
256 {
257     //emit a table row
258     var dname = displayname(name);
259     var ln = create_element('b', dname);
260     var tln = document.createElement('td');
261     tln.width = '90';
262     tln.vAlign = 'top';
263     tln.appendChild(ln);
264     var tr = document.createElement('tr');
265     tr.appendChild(tln);
266     tr.appendChild(dn);
267     return tr;
268 }
269
270 function cleanurl(v)
271 {
272     var v1 = v;
273     var v2 = v1.replace(/\?.*$/, "");
274     var v3 = v2.replace(/http:\/\//, "");
275     return v3;
276 }
277
278 function paint_data_elements(target, node)
279 {
280     var nodes = node.childNodes;
281     var dn = 0;
282     var lastname = '';
283     var i;
284     for (i = 0; i < nodes.length; i++)
285     {
286         if (nodes[i].nodeType != 1)
287             continue;
288         var name = nodes[i].nodeName;
289         if (name == 'recid' || name == 'md-title')
290             continue;
291         if (name != lastname && lastname != 'location') 
292         {
293             if (dn)
294             {
295                 var tr = paint_details_tr(lastname, dn);
296                 target.appendChild(tr);
297             }
298             dn = document.createElement('td');
299             lastname = name;
300         }
301         if (name == 'location')
302         {
303             dn = document.createElement('td');
304             dn.appendChild(paint_subrecord(nodes[i]));
305             target.appendChild(paint_details_tr('Location', dn)); 
306             continue;
307         }
308         if (!nodes[i].childNodes[0])
309                 continue;
310         var value = nodes[i].childNodes[0].nodeValue;
311         if (dn.childNodes[0])
312             dn.appendChild(document.createTextNode('; '));
313         var hyl = hyperlink_field(name);
314         var nv;
315         if (hyl)
316         {
317             nv = create_element('a', cleanurl(value));
318             if (hyl == 'URL')
319             {
320                 nv.href = value;
321                 nv.target = '_blank';
322             }
323             else
324             {
325                 nv.href = '#';
326                 nv.setAttribute('term', value);
327                 nv.setAttribute('searchfield', hyl);
328                 nv.onclick = function() { hyperlink_search(this); return false; };
329             }
330         }
331         else if (name == 'md-lccn')
332         {
333             nv = document.createElement('span');
334             nv.appendChild(document.createTextNode(value + ' '));
335             var link = create_element('a', 'Show title in LoC');
336                 link.setAttribute('target', '_blank');
337                 link.setAttribute('href', 'http://catalog.loc.gov/cgi-bin/Pwebrecon.cgi?DB=local&CNT=10&CMD=10+records+per+page&CMD=lccn+' + value);
338             nv.appendChild(link);
339
340         }
341         else
342             nv = document.createTextNode(value);
343         dn.appendChild(nv);
344     }
345     if (dn && lastname != 'location')
346     {
347         var tr = paint_details_tr(lastname, dn);
348         target.appendChild(tr);
349     }
350 }
351
352 function paint_subrecord(node)
353 {
354     var table = document.createElement('table');
355     var tbody = document.createElement('tbody');
356     var zurl = node.getAttribute('id');
357     var name = node.getAttribute('name');
358     var tr;
359     var td;
360     if (name)
361         td = create_element('td', name);
362     else
363         td = create_element('td', zurl);
364     tr = paint_details_tr('Source', td);
365     tbody.appendChild(tr);
366     paint_data_elements(tbody, node);
367     table.appendChild(tbody);
368     return table;
369 }
370
371 function paint_details(body, xml)
372 {
373     clear_cell(body);
374     var table = document.createElement('table');
375     var tbody = document.createElement('tbody');
376     table.setAttribute('cellpadding', 2);
377     paint_data_elements(tbody, xml.childNodes[0]);
378     table.appendChild(tbody);
379     body.appendChild(table);
380     body.style.display = 'inline';
381 }
382
383 function show_details()
384 {
385     if (xfetchDetails.readyState != 4)
386         return;
387     var xml = xfetchDetails.responseXML;
388     var error = xml.getElementsByTagName("error");
389     if (error[0])
390     {
391         var msg = error[0].childNodes[0].nodeValue;
392         alert(msg);
393         location = "?";
394         return;
395     }
396
397     var idn = xml.getElementsByTagName('recid');
398     if (!idn[0])
399         return;
400     var id = idn[0].childNodes[0].nodeValue;
401     cur_id = id;
402     cur_rec = xml;
403
404     var nodes = document.getElementsByName('listrecord');
405     var i;
406     for (i = 0; i < nodes.length; i++)
407     {
408         var dets = nodes[i].getElementsByTagName('div');
409         if (dets[0])
410             dets[0].style.display = 'none';
411     }
412
413     var body = document.getElementById('rec_' + id);
414     if (!body)
415         return;
416     paint_details(body, xml);
417 }
418
419 function hyperlink_search(obj)
420 {
421     var field = obj.getAttribute('searchfield');
422     var term = obj.getAttribute('term');
423     var queryfield  = document.getElementById('query');
424     queryfield.value = field + '="' + term + '"';
425     start_search();
426 }
427
428 function fetch_details(id)
429 {
430     cur_id = -1;
431     if (id == cur_id)
432     {
433         cur_id = -1;
434         return;
435     }
436     if (!session)
437         return;
438     var url = "search.pz2?session=" + session +
439         "&command=record" +
440         "&id=" + id;
441     SendXmlHttpObject(xfetchDetails = GetXmlHttpObject(), url, show_details);
442 }
443
444 function show_records()
445 {
446     if (xshow.readyState != 4)
447         return;
448     var i;
449     var xml = xshow.responseXML;
450     var body = document.getElementById("body");
451     var hits = xml.getElementsByTagName("hit");
452
453     clear_cell(body);
454
455     if (!hits[0]) // We should never get here with blocking operations
456     {
457         assign_text(body, 'No records yet');
458         searchtimer = setTimeout(check_search, 250);
459     }
460     else
461     {
462         var total = Number(xml.getElementsByTagName('total')[0].childNodes[0].nodeValue);
463         var merged = Number(xml.getElementsByTagName('merged')[0].childNodes[0].nodeValue);
464         var start = Number(xml.getElementsByTagName('start')[0].childNodes[0].nodeValue);
465         var num = Number(xml.getElementsByTagName('num')[0].childNodes[0].nodeValue);
466         var clients = Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
467         var pager = make_pager(merged, start,recstoshow);
468         var break_node1 = document.createElement('br');
469         var break_node2 = document.createElement('br');
470         var record_container = document.createElement('div');
471         var interval = create_element('div', 'Records : ' + (start + 1) +
472                                              ' to ' + (start + num) + ' of ' +
473                                              merged + ' (total hits: ' +
474                                              total + ')');
475         searched = 1;
476         interval.className = 'results';
477         record_container.className = 'records';
478
479         body.appendChild(pager);
480         body.appendChild(interval);
481         body.appendChild(break_node1);
482         body.appendChild(break_node2);
483         body.appendChild(record_container);
484
485         for (i = 0; i < hits.length; i++)
486         {
487             var tn = hits[i].getElementsByTagName("md-title");
488             var title = '';
489             var an = hits[i].getElementsByTagName("md-author");
490             var author = '';
491             var cn = hits[i].getElementsByTagName("count");
492             var count = 1;
493             var idn = hits[i].getElementsByTagName("recid");
494
495             if (tn[0] && tn[0].childNodes[0]) {
496                 title = tn[0].childNodes[0].nodeValue;
497             } else {
498                 title = 'N/A';
499             }
500             if (an[0] && an[0].childNodes[0])
501                     author = an[0].childNodes[0].nodeValue;
502             if (cn[0])
503                 count = Number(cn[0].childNodes[0].nodeValue);
504             var id = idn[0].childNodes[0].nodeValue;
505             
506             var record_div = document.createElement('div');
507             record_div.className = 'record';
508             record_div.setAttribute('name', 'listrecord');
509
510             var record_cell = create_element('a', title);
511             record_cell.setAttribute('href', '#' + id);
512             record_cell.setAttribute('id', id);
513             //record_cell.onclick = function() { fetch_details(this.getAttribute('id')); return false; }
514             record_cell.onclick = function() { fetch_details(this.getAttribute('id')); return false; };
515             record_div.appendChild(record_cell);
516             if (author)
517             {
518                 record_div.appendChild(document.createTextNode(', by '));
519                 var al = create_element('a', author);
520                 al.setAttribute('href', '#');
521                 al.setAttribute('term', author);
522                 al.setAttribute('searchfield', 'au');
523                 al.onclick = function() { hyperlink_search(this); return false; };
524                 record_div.appendChild(al);
525             }
526             if (count > 1)
527                 record_div.appendChild(document.createTextNode(
528                         ' (' + count + ')'));
529             var det_div = document.createElement('div');
530             if (id == cur_id)
531                 paint_details(det_div, cur_rec);
532             else
533                 det_div.style.display = 'none';
534             det_div.setAttribute('id', 'rec_' + id);
535             det_div.setAttribute('name', 'details');
536             record_div.appendChild(det_div);
537             record_container.appendChild(record_div);
538         }
539
540         shown++;
541         if (clients > 0)
542         {
543             if (shown < 5)
544                 searchtimer = setTimeout(check_search, 1000);
545             else
546                 searchtimer = setTimeout(check_search, 2000);
547         }
548     }
549     if (!termtimer)
550         termtimer = setTimeout(check_termlist, 500);
551 }
552
553 function check_search()
554 {
555     clearTimeout(searchtimer);
556     var url = "search.pz2?" +
557         "command=show" +
558         "&start=" + document.search.startrec.value +
559         "&num=" + recstoshow +
560         "&session=" + session +
561         "&sort=" + cur_sort +
562         "&block=1";
563     xshow = GetXmlHttpObject();
564     xshow.onreadystatechange=show_records;
565     xshow.open("GET", url);
566     xshow.send(null);
567 }
568
569
570 function refine_query (obj) {
571     var term = obj.getAttribute('term');
572     var cur_termlist = obj.getAttribute('facet');
573     var query_cell = document.getElementById('query');
574     
575     term = term.replace(/[\(\)]/g, '');
576     
577     if (cur_termlist == 'subject')
578         query_cell.value += ' and su="' + term + '"';
579     else if (cur_termlist == 'author')
580         query_cell.value += ' and au="' + term + '"';
581     else if (cur_termlist == 'date')
582         query_cell.value += ' and date="' + term + '"';
583
584     start_search();
585 }
586
587 function clear_termlists()
588 {
589     var i;
590     for (i = 0; i < facet_list.length; i++)
591         clear_cell(facet_list[i][1]);
592 }
593
594 function show_termlists()
595 {
596     if (xtermlist.readyState != 4)
597         return;
598
599     var i;
600     var xml = xtermlist.responseXML;
601     var clients =
602         Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
603     var lists = xml.getElementsByTagName("list");
604
605     for (i = 0; i < lists.length; i++)
606     {
607         var listname = lists[i].getAttribute('name');
608         var body = document.getElementById('facet_' + listname + '_terms');
609         if (body.style.display == 'none')
610             continue;
611         clear_cell(body);
612         var terms = lists[i].getElementsByTagName('term');
613         var t;
614         for (t = 0; t < terms.length; t++)
615         {
616             var namen = terms[t].getElementsByTagName("name");
617             var freqn = terms[t].getElementsByTagName("frequency");
618             if (namen[0])
619                 var term = namen[0].childNodes[0].nodeValue;
620                 var freq = freqn[0].childNodes[0].nodeValue;
621                 var refine_cell = create_element('a', term + ' (' + freq + ')');
622                 refine_cell.setAttribute('href', '#');
623                 refine_cell.setAttribute('term', term);
624                 refine_cell.setAttribute('facet', listname);
625                 refine_cell.onclick = function () {
626                     refine_query(this);
627                     return false;
628                 };
629                 body.appendChild(refine_cell);
630         }
631     }
632     if (clients > 0)
633         termtimer = setTimeout(check_termlist, 1000);
634 }
635
636 function check_termlist()
637 {
638     var facet_names = '';
639     var i;
640     for (i = 0; i < facet_list.length; i++)
641         if (facet_list[i][1].style.display != 'none')
642         {
643             if (facet_names)
644                 facet_names += ',';
645             facet_names += facet_list[i][0];
646         }
647     var url = "search.pz2?" +
648         "command=termlist" +
649         "&session=" + session +
650         "&name=" + facet_names +
651         "&num=12";
652     SendXmlHttpObject(xtermlist = GetXmlHttpObject(), url, show_termlists);
653 }
654
655 function show_stat()
656 {
657     if (xstat.readyState != 4)
658         return;
659     var i;
660     var xml = xstat.responseXML;
661     var body = document.getElementById("stat");
662     var nodes = xml.childNodes[0].childNodes;
663     var clients =
664         Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
665     if (!nodes[0])
666     {
667         stattimer  = setTimeout(check_stat, 500);
668     }
669     else
670     {
671         assign_text(body, '(');
672         for (i = 0; i < nodes.length; i++)
673         {
674             if (nodes[i].nodeType != 1)
675                 continue;
676             var value = nodes[i].childNodes[0].nodeValue;
677             if (value == 0)
678                 continue;
679             var name = nodes[i].nodeName;
680             append_text(body, ' ' + name + '=' + value);
681         }
682
683         append_text(body, ')');
684         if (clients > 0)
685             stattimer = setTimeout(check_stat, 2000);
686     }
687 }
688
689 function check_stat()
690 {
691     var url = "search.pz2?" +
692         "command=stat" +
693         "&session=" + session;
694     xstat = GetXmlHttpObject();
695     xstat.onreadystatechange=show_stat;
696     xstat.open("GET", url);
697     xstat.send(null);
698 }
699
700 function search_started()
701 {
702     if (xsearch.readyState != 4)
703         return;
704     var xml = xsearch.responseXML;
705     var error = xml.getElementsByTagName("error");
706     if (error[0])
707     {
708         var msg = error[0].childNodes[0].nodeValue;
709         alert(msg);
710         return;
711     }
712     check_search();
713     stattimer = setTimeout(check_stat, 1000);
714 }
715
716 function start_search()
717 {
718     clearTimeout(termtimer);
719     termtimer = 0;
720     clearTimeout(searchtimer);
721     searchtimer = 0;
722     clearTimeout(stattimer);
723     stattimer = 0;
724     clearTimeout(showtimer);
725     showtimer = 0;
726     cur_id = -1;
727     clear_termlists();
728     var query = escape(document.getElementById('query').value);
729     var url = "search.pz2?" +
730         "command=search" +
731         "&session=" + session +
732         "&query=" + query;
733     xsearch = GetXmlHttpObject();
734     xsearch.onreadystatechange=search_started;
735     xsearch.open("GET", url);
736     xsearch.send(null);
737     clear_cell(document.getElementById("body"));
738     update_history();
739     shown = 0;
740     document.search.startrec.value = 0;
741 }
742
743 function session_encode ()
744 {
745     var i;
746     var session = '';
747
748     for (i = 0; i < session_cells.length; i++)
749     {
750         var name = session_cells[i];
751         var value = escape(document.getElementById(name).value);
752         session += '&' + name + '=' + value;
753     }
754
755     return session;
756 }
757
758
759 function session_restore (session)
760 {
761     var fields = session.split(/&/);
762     var i;
763
764     for (i = 1; i < fields.length; i++)
765     {
766         var pair = fields[i].split(/=/);
767         var key = pair.shift();
768         var value = pair.join('=');
769         var cell = document.getElementById(key);
770
771         cell.value = value;
772     }
773     
774 }
775
776
777 function session_read ()
778 {
779     var ses = window.location.hash.replace(/^#/, '');
780     return ses;
781 }
782
783
784 function session_store (new_value)
785 {
786     window.location.hash = '#' + new_value;
787 }
788
789
790 function update_history ()
791 {
792     var session = session_encode();
793     session_store(session);
794     old_session = session;
795 }
796
797
798 function session_check ()
799 {
800     var session = session_read();
801     var action = document.search.action_type.value;
802
803     clearInterval(url_surveillence);
804
805     if ( session != unescape(old_session) )
806     {
807         session_restore(session);
808
809         if (action == 'search') {
810             start_search();
811         } else if (action == 'page') {
812             check_search();
813         } else {
814             alert('Unregocnized action_type: ' + action);
815             return;
816         }
817     }
818     
819     url_surveillence = setInterval(session_check, 200);
820 }
821
822
823 function get_available_facets () {
824     var facet_container = document.getElementById('termlists');
825     var facet_cells = facet_container.childNodes;
826     var facets = Array();
827     var i;
828
829     for (i = 0; i < facet_cells.length; i++) {
830         var cell = facet_cells.item(i);
831
832         if (cell.className == 'facet') {
833             var facet_name = cell.id.replace(/^facet_([^_]+)_terms$/, "$1");
834             facets.push(Array(facet_name, cell));
835         }
836     }
837
838     return facets;
839 }
840
841
842 function get_facet_container (obj) {
843     return document.getElementById(obj.id + '_terms');
844 }
845
846
847 function toggle_facet (obj) {
848     var container = get_facet_container(obj);
849
850     if (obj.className == 'selected') {
851         obj.className = 'unselected';
852         container.style.display = 'inline';
853         check_termlist();
854     } else {
855         obj.className = 'selected';
856         container.style.display = 'none';
857     }
858 }