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