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