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