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