Top-aligned data label
[pazpar2-moved-to-github.git] / www / demo / search.js
1 /* $Id: search.js,v 1.31 2007-01-16 19:24:44 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         location = "?";
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
226         return name;
227 }
228
229 function  paint_details_tr(name, dn)
230 {
231     //emit a table row
232     var dname = displayname(name);
233     var ln = create_element('b', dname);
234     var tln = document.createElement('td');
235     tln.setAttribute('width', 70);
236     tln.setAttribute('valign', 'top');
237     tln.appendChild(ln);
238     var tr = document.createElement('tr');
239     tr.appendChild(tln);
240     tr.appendChild(dn);
241     return tr;
242 }
243
244 function paint_details(body, xml)
245 {
246     clear_cell(body);
247     //body.appendChild(document.createElement('br'));
248     var nodes = xml.childNodes[0].childNodes;
249     var i;
250     var table = document.createElement('table');
251     table.setAttribute('cellpadding', 2);
252     var dn = 0;
253     var lastname = '';
254     for (i = 0; i < nodes.length; i++)
255     {
256         if (nodes[i].nodeType != 1)
257             continue;
258         var name = nodes[i].nodeName;
259         if (name == 'recid' || name == 'md-title')
260             continue;
261         if (name != lastname)
262         {
263             if (dn)
264             {
265                 var tr = paint_details_tr(lastname, dn);
266                 table.appendChild(tr);
267             }
268             dn = document.createElement('td');
269             lastname = name;
270         }
271
272         if (!nodes[i].childNodes[0])
273                 continue;
274         var value = nodes[i].childNodes[0].nodeValue;
275         if (dn.childNodes[0])
276             value = '; ' + value;
277         var nv = document.createTextNode(value);
278         dn.appendChild(nv);
279     }
280     if (dn)
281     {
282         var tr = paint_details_tr(lastname, dn);
283         table.appendChild(tr);
284     }
285     body.appendChild(table);
286     body.style.display = 'inline';
287 }
288
289 function show_details()
290 {
291     if (xfetchDetails.readyState != 4)
292         return;
293     var xml = xfetchDetails.responseXML;
294     var error = xml.getElementsByTagName("error");
295     if (error[0])
296     {
297         var msg = error[0].childNodes[0].nodeValue;
298         alert(msg);
299         location = "?";
300         return;
301     }
302
303     // This is some ugly display code. Replace with your own ting o'beauty
304
305     var idn = xml.getElementsByTagName('recid');
306     if (!idn[0])
307         return;
308     var id = idn[0].childNodes[0].nodeValue;
309     cur_id = id;
310     cur_rec = xml;
311
312     var body = document.getElementById('rec_' + id);
313     if (!body)
314         return;
315     paint_details(body, xml);
316 }
317
318 function hyperlink_search(field, obj)
319 {
320     var term = obj.getAttribute('term');
321     var queryfield  = document.getElementById('query');
322     queryfield.value = field + '=' + term;
323     start_search();
324 }
325
326 function fetch_details(id)
327 {
328     cur_id = -1;
329     var nodes = document.getElementsByName('listrecord');
330     var i;
331     for (i = 0; i < nodes.length; i++)
332     {
333         var dets = nodes[i].getElementsByTagName('div');
334         if (dets[0])
335             dets[0].style.display = 'none';
336     }
337     if (id == cur_id)
338     {
339         cur_id = -1;
340         return;
341     }
342     if (!session)
343         return;
344     var url = "search.pz2?session=" + session +
345         "&command=record" +
346         "&id=" + id;
347     SendXmlHttpObject(xfetchDetails = GetXmlHttpObject(), url, show_details);
348 }
349
350 function show_records()
351 {
352     if (xshow.readyState != 4)
353         return;
354     var i;
355     var xml = xshow.responseXML;
356     var body = document.getElementById("body");
357     var hits = xml.getElementsByTagName("hit");
358
359     clear_cell(body);
360
361     if (!hits[0]) // We should never get here with blocking operations
362     {
363         assign_text(body, 'No records yet');
364         searchtimer = setTimeout(check_search, 250);
365     }
366     else
367     {
368         var total = Number(xml.getElementsByTagName('total')[0].childNodes[0].nodeValue);
369         var merged = Number(xml.getElementsByTagName('merged')[0].childNodes[0].nodeValue);
370         var start = Number(xml.getElementsByTagName('start')[0].childNodes[0].nodeValue);
371         var num = Number(xml.getElementsByTagName('num')[0].childNodes[0].nodeValue);
372         var clients = Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
373         var pager = make_pager(merged, start,recstoshow);
374         var break_node1 = document.createElement('br');
375         var break_node2 = document.createElement('br');
376         var record_container = document.createElement('div');
377         var interval = create_element('div', 'Records : ' + (start + 1) +
378                                              ' to ' + (start + num) + ' of ' +
379                                              merged + ' (total hits: ' +
380                                              total + ')');
381         searched = 1;
382         interval.className = 'results';
383         record_container.className = 'records';
384
385         body.appendChild(pager);
386         body.appendChild(interval);
387         body.appendChild(break_node1);
388         body.appendChild(break_node2);
389         body.appendChild(record_container);
390
391         for (i = 0; i < hits.length; i++)
392         {
393             var tn = hits[i].getElementsByTagName("md-title");
394             var title = '';
395             var an = hits[i].getElementsByTagName("md-author");
396             var author = '';
397             var cn = hits[i].getElementsByTagName("count");
398             var count = 1;
399             var idn = hits[i].getElementsByTagName("recid");
400
401             if (tn[0]) {
402                 title = tn[0].childNodes[0].nodeValue;
403             } else {
404                 title = 'N/A';
405             }
406             if (an[0] && an[0].childNodes[0])
407                     author = an[0].childNodes[0].nodeValue;
408             if (cn[0])
409                 count = Number(cn[0].childNodes[0].nodeValue);
410             var id = idn[0].childNodes[0].nodeValue;
411             
412             var record_div = document.createElement('div');
413             record_div.className = 'record';
414             record_div.setAttribute('name', 'listrecord');
415
416             var record_cell = create_element('a', title);
417             record_cell.setAttribute('href', '#');
418             record_cell.setAttribute('onclick', 'fetch_details(' + id + '); return false');
419             record_div.appendChild(record_cell);
420             if (author)
421             {
422                 record_div.appendChild(document.createTextNode(', by '));
423                 var al = create_element('a', author);
424                 al.setAttribute('href', '#');
425                 al.setAttribute('term', author);
426                 al.onclick = function() { hyperlink_search('au', this); return false; };
427                 record_div.appendChild(al);
428             }
429             if (count > 1)
430                 record_div.appendChild(document.createTextNode(
431                         ' (' + count + ')'));
432             var det_div = document.createElement('div');
433             if (id == cur_id)
434                 paint_details(det_div, cur_rec);
435             else
436                 det_div.style.display = 'none';
437             det_div.setAttribute('id', 'rec_' + id);
438             det_div.setAttribute('name', 'details');
439             record_div.appendChild(det_div);
440             record_container.appendChild(record_div);
441         }
442
443         shown++;
444         if (clients > 0)
445         {
446             if (shown < 5)
447                 searchtimer = setTimeout(check_search, 1000);
448             else
449                 searchtimer = setTimeout(check_search, 2000);
450         }
451     }
452     if (!termtimer)
453         termtimer = setTimeout(check_termlist, 500);
454 }
455
456 function check_search()
457 {
458     clearTimeout(searchtimer);
459     var url = "search.pz2?" +
460         "command=show" +
461         "&start=" + document.search.startrec.value +
462         "&num=" + recstoshow +
463         "&session=" + session +
464         "&sort=" + cur_sort +
465         "&block=1";
466     xshow = GetXmlHttpObject();
467     xshow.onreadystatechange=show_records;
468     xshow.open("GET", url);
469     xshow.send(null);
470 }
471
472
473 function refine_query (obj) {
474     var term = obj.getAttribute('term');
475     var cur_termlist = obj.getAttribute('facet');
476     var query_cell = document.getElementById('query');
477     
478     term = term.replace(/[\(\)]/g, '');
479     
480     if (cur_termlist == 'subject')
481         query_cell.value += ' and su=(' + term + ')';
482     else if (cur_termlist == 'author')
483         query_cell.value += ' and au=(' + term + ')';
484
485     start_search();
486 }
487
488
489
490 function show_termlist()
491 {
492     if (xtermlist.readyState != 4)
493         return;
494
495     var i;
496     var xml = xtermlist.responseXML;
497     var body = facet_list[cur_facet][1];
498     var facet_name = facet_list[cur_facet][0];
499     var hits = xml.getElementsByTagName("term");
500     var clients =
501         Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
502
503     cur_facet++;
504
505     if (cur_facet >= facet_list.length)
506         cur_facet = 0;
507
508     if (!hits[0])
509     {
510         termtimer = setTimeout(check_termlist, 500);
511     }
512     else
513     {
514         clear_cell(body);
515         
516         for (i = 0; i < hits.length; i++)
517         {
518             var namen = hits[i].getElementsByTagName("name");
519             var freqn = hits[i].getElementsByTagName("frequency");
520             if (namen[0])
521                 var term = namen[0].childNodes[0].nodeValue;
522                 var freq = freqn[0].childNodes[0].nodeValue;
523                 var refine_cell = create_element('a', term + ' (' + freq + ')');
524                 refine_cell.setAttribute('href', '#');
525                 refine_cell.setAttribute('term', term);
526                 refine_cell.setAttribute('facet', facet_name);
527                 refine_cell.onclick = function () {
528                     refine_query(this);
529                     return false;
530                 };
531                 body.appendChild(refine_cell);
532         }
533
534         if (clients > 0)
535             termtimer = setTimeout(check_termlist, 1000);
536     }
537 }
538
539 function check_termlist()
540 {
541     var facet_name = facet_list[cur_facet][0];
542     var url = "search.pz2?" +
543         "command=termlist" +
544         "&session=" + session +
545         "&name=" + facet_name;
546     xtermlist = GetXmlHttpObject();
547     xtermlist.onreadystatechange=show_termlist;
548     xtermlist.open("GET", url);
549     xtermlist.send(null);
550 }
551
552 function show_stat()
553 {
554     if (xstat.readyState != 4)
555         return;
556     var i;
557     var xml = xstat.responseXML;
558     var body = document.getElementById("stat");
559     var nodes = xml.childNodes[0].childNodes;
560     var clients =
561         Number(xml.getElementsByTagName("activeclients")[0].childNodes[0].nodeValue);
562     if (!nodes[0])
563     {
564         stattimer  = setTimeout(check_stat, 500);
565     }
566     else
567     {
568         assign_text(body, '(');
569         for (i = 0; i < nodes.length; i++)
570         {
571             if (nodes[i].nodeType != 1)
572                 continue;
573             var value = nodes[i].childNodes[0].nodeValue;
574             if (value == 0)
575                 continue;
576             var name = nodes[i].nodeName;
577             append_text(body, ' ' + name + '=' + value);
578         }
579
580         append_text(body, ')');
581         if (clients > 0)
582             stattimer = setTimeout(check_stat, 2000);
583     }
584 }
585
586 function check_stat()
587 {
588     var url = "search.pz2?" +
589         "command=stat" +
590         "&session=" + session;
591     xstat = GetXmlHttpObject();
592     xstat.onreadystatechange=show_stat;
593     xstat.open("GET", url);
594     xstat.send(null);
595 }
596
597 function search_started()
598 {
599     if (xsearch.readyState != 4)
600         return;
601     var xml = xsearch.responseXML;
602     var error = xml.getElementsByTagName("error");
603     if (error[0])
604     {
605         var msg = error[0].childNodes[0].nodeValue;
606         alert(msg);
607         return;
608     }
609     check_search();
610     stattimer = setTimeout(check_stat, 1000);
611 }
612
613 function start_search()
614 {
615     clearTimeout(termtimer);
616     termtimer = 0;
617     clearTimeout(searchtimer);
618     searchtimer = 0;
619     clearTimeout(stattimer);
620     stattimer = 0;
621     clearTimeout(showtimer);
622     showtimer = 0;
623     cur_id = -1;
624     var query = escape(document.getElementById('query').value);
625     var url = "search.pz2?" +
626         "command=search" +
627         "&session=" + session +
628         "&query=" + query;
629     xsearch = GetXmlHttpObject();
630     xsearch.onreadystatechange=search_started;
631     xsearch.open("GET", url);
632     xsearch.send(null);
633     clear_cell(document.getElementById("body"));
634     update_history();
635     shown = 0;
636     document.search.startrec.value = 0;
637 }
638
639 function session_encode ()
640 {
641     var i;
642     var session = '';
643
644     for (i = 0; i < session_cells.length; i++)
645     {
646         var name = session_cells[i];
647         var value = escape(document.getElementById(name).value);
648         session += '&' + name + '=' + value;
649     }
650
651     return session;
652 }
653
654
655 function session_restore (session)
656 {
657     var fields = session.split(/&/);
658     var i;
659
660     for (i = 1; i < fields.length; i++)
661     {
662         var pair = fields[i].split(/=/);
663         var key = pair.shift();
664         var value = pair.join('=');
665         var cell = document.getElementById(key);
666
667         cell.value = value;
668     }
669     
670 }
671
672
673 function session_read ()
674 {
675     var ses = window.location.hash.replace(/^#/, '');
676     return ses;
677 }
678
679
680 function session_store (new_value)
681 {
682     window.location.hash = '#' + new_value;
683 }
684
685
686 function update_history ()
687 {
688     var session = session_encode();
689     session_store(session);
690     old_session = session;
691 }
692
693
694 function session_check ()
695 {
696     var session = session_read();
697     var action = document.search.action_type.value;
698
699     clearInterval(url_surveillence);
700
701     if ( session != unescape(old_session) )
702     {
703         session_restore(session);
704
705         if (action == 'search') {
706             start_search();
707         } else if (action == 'page') {
708             check_search();
709         } else {
710             alert('Unregocnized action_type: ' + action);
711             return;
712         }
713     }
714     
715     url_surveillence = setInterval(session_check, 200);
716 }
717
718
719 function get_available_facets () {
720     var facet_container = document.getElementById('termlists');
721     var facet_cells = facet_container.childNodes;
722     var facets = Array();
723     var i;
724
725     for (i = 0; i < facet_cells.length; i++) {
726         var cell = facet_cells.item(i);
727
728         if (cell.className == 'facet') {
729             var facet_name = cell.id.replace(/^facet_([^_]+)_terms$/, "$1");
730             facets.push(Array(facet_name, cell));
731         }
732     }
733
734     return facets;
735 }
736
737
738 function get_facet_container (obj) {
739     return document.getElementById(obj.id + '_terms');
740 }
741
742
743 function toggle_facet (obj) {
744     var container = get_facet_container(obj);
745
746     if (obj.className == 'selected') {
747         obj.className = 'unselected';
748         container.style.display = 'inline';
749     } else {
750         obj.className = 'selected';
751         container.style.display = 'none';
752     }
753 }