Internal query handling completely rewritten.
[pazpar2-moved-to-github.git] / www / masterkey / js / client.js
1 /*
2 ** $Id: client.js,v 1.10 2007-04-02 15:50:27 jakub Exp $
3 ** MasterKey - pazpar2's javascript client .
4 */
5
6 /* start with creating pz2 object and passing it event handlers*/
7 var my_paz = new pz2( { "onshow": my_onshow,
8                     //"showtime": 1000,
9                     //"onstat": my_onstat,
10                     "onterm": my_onterm,
11                     "termlist": "xtargets,subject,author,date",
12                     //"onbytarget": my_onbytarget,
13                     "onrecord": my_onrecord } );
14
15 /* some state variable */
16 var currentSort = 'relevance';
17 var currentResultsPerPage = 20;
18 /*var currentQuery = null;
19 var currentQueryArr = new Array();*/
20 var currentPage = 0;
21 var curQuery = new pzQuery();
22 /*var currentFilter = undefined;*/
23 /*var currentFilterName = null;*/
24
25 var currentDetailedId = null;
26 var currentDetailedData = null;
27
28 var termStartup = true;
29 var advancedOn = false;
30
31 /* wait until the DOM is ready and register basic handlers */
32 $(document).ready( function() { 
33                     document.search.onsubmit = onFormSubmitEventHandler;
34
35                     document.search.query.value = '';
36                     document.search.title.value = '';
37                     document.search.author.value = '';
38                     document.search.subject.value = '';
39                     document.search.date.value = '';
40                     
41                     $('#advanced').click(toggleAdvanced);
42
43                     $('#sort').change(function(){ 
44                         currentSort = this.value;
45                         currentPage = 0;
46                         my_paz.show(0, currentResultsPerPage, currentSort);
47                     });
48                     
49                     $('#perpage').change(function(){ 
50                         currentResultsPerPage = this.value;
51                         currentPage = 0;
52                         my_paz.show(0, currentResultsPerPage, currentSort);
53                     });
54 } );
55
56 /* search button event handler */
57 function onFormSubmitEventHandler() {
58     loadQueryFromForm();
59     curQuery.clearFilter();
60     fireSearch();
61     drawBreadcrumb();
62     $('div.content').show();
63     $("div.leftbar").show();
64     return false;
65 }
66
67 /*
68 *********************************************************************************
69 ** pz2 Event Handlers ***********************************************************
70 *********************************************************************************
71 */
72
73 /*
74 ** data.hits["md-title"], data.hits["md-author"], data.hits.recid, data.hits.count
75 ** data.activeclients, data.merged, data.total, data.start, data.num 
76 */
77 function my_onshow(data)
78 {
79     var recsBody = $('div.records');
80     recsBody.empty();
81     
82     for (var i = 0; i < data.hits.length; i++) {
83         var title = data.hits[i]["md-title"] || 'N/A';
84         var author = data.hits[i]["md-author"] || '';
85         var id = data.hits[i].recid;
86         var count = data.hits[i].count || 1;
87         
88         var recBody = $('<div class="record" id="rec_'+id+'"></div>');
89         var aTitle = $('<a class="recTitle">'+title+'</a>').appendTo(recBody);
90         aTitle.click(function(){
91                         var clickedId = this.parentNode.id.split('_')[1];
92                         if(currentDetailedId == clickedId){
93                             $(this.parentNode.lastChild).remove();
94                             currentDetailedId = null;
95                             return;
96                         } else if (currentDetailedId != null) {
97                             $('#rec_'+currentDetailedId).children('.detail').remove();
98                         }
99                         currentDetailedId = clickedId;
100                         my_paz.record(currentDetailedId);
101                         });
102         
103         if( author ) {
104             recBody.append('<i> by </i>');
105             $('<a name="author" class="recAuthor">'+author+'</a>\n').click(function(){ 
106                             refine(this.name, this.firstChild.nodeValue) }).appendTo(recBody);
107         }
108
109         if( currentDetailedId == id ) {
110             var detailBox = $('<div class="detail"></div>').appendTo(recBody);
111             drawDetailedRec(detailBox);
112         }
113
114         if( count > 1 ) {
115             recBody.append('<span> ('+count+')</span>');
116         }
117
118         recsBody.append('<div class="resultNum">'+(currentPage*currentResultsPerPage+i+1)+'.</a>');
119         recsBody.append(recBody);
120     }
121     drawPager(data.merged, data.total);    
122 }
123
124 /*
125 ** data.activeclients, data.hits, data.records, data.clients, data.searching
126 */
127 function my_onstat(data){}
128
129 /*
130 ** data[listname]: name, freq, [id]
131 */
132 function my_onterm(data)
133 {
134     var termLists = $("#termlists");
135
136     if(termStartup)
137     {
138         for(var key in data){
139             if (key == "activeclients")
140                 continue;
141             var listName = key;
142             var listClass = "unselected";
143
144             if (key == "xtargets"){
145                 listName = "resource";
146                 listClass = "selected";
147             }
148
149             var termList = $('<div class="termlist" id="term_'+key+'"/>').appendTo(termLists);
150             var termTitle = $('<div class="termTitle"><a class="'+listClass+'">'+listName+'</a></div>').appendTo(termList);
151             termTitle.click(function(){
152                                 if( this.firstChild.className == "selected" ){
153                                     this.firstChild.className = "unselected";
154                                     $(this.nextSibling).hide();
155                                 } else {
156                                     this.firstChild.className = "selected";
157                                     $(this.nextSibling).show();
158                                 }
159                             });
160
161             listEntries = $('<div class="termEntries"></div>');
162             if (key != "xtargets") listEntries.hide();
163             listEntries.appendTo(termList);
164
165             for(var i = 0; i < data[key].length; i++)
166             {
167                 if (key == "xtargets"){
168                     var listItem = $('<a class="sub" name="xtarget" value="'+data[key][i].id+'">'+data[key][i].name
169                             /*+'<span> ('+data[key][i].freq+')</span>'*/+'</a>');
170                     listItem.click(function(){ 
171                         refine(this.name, this.attributes[0].nodeValue, this.firstChild.nodeValue) });
172                     listItem.appendTo(listEntries);
173                 } else {
174                     var listItem = $('<a class="sub" name="'+key+'">'+data[key][i].name
175                             /*+'<span> ('+data[key][i].freq+')</span>'*/+'</a>');
176                     listItem.click(function(){ refine(this.name, this.firstChild.nodeValue) });
177                     listItem.appendTo(listEntries);
178                 }
179             }        
180             $('<hr/>').appendTo(termLists);
181         }
182         termStartup = false;
183     } 
184     else 
185     {
186         for(var key in data){
187             if (key == "activeclients")
188                 continue;
189             var listEntries = $('#term_'+key).children('.termEntries');
190             listEntries.empty()
191
192             for(var i = 0; i < data[key].length; i++){
193                 if (key == "xtargets"){
194                     var listItem = $('<a class="sub" name="xtarget" value="'+data[key][i].id+'">'+data[key][i].name
195                                 /*+'<span> ('+data[key][i].freq+')</span>'*/+'</a>').click(function(){ 
196                                     refine(this.name, this.attributes[0].nodeValue, this.firstChild.nodeValue) });
197                     listItem.appendTo(listEntries);
198                 } else {
199                     var listItem = $('<a class="sub" name="'+key+'">'+data[key][i].name
200                                 /*+'<span> ('+data[key][i].freq+')</span>'*/+'</a>').click(function(){ 
201                                                                         refine(this.name, this.firstChild.nodeValue) });
202                     listItem.appendTo(listEntries);
203                 }
204             }         
205         }
206     }
207 }
208
209 /*
210 ** data["md-title"], data["md-date"], data["md-author"], data["md-subject"], data["location"][0].name
211 */
212 function my_onrecord(data)
213 {
214     currentDetailedData = data;
215     drawDetailedRec();
216 }
217
218 /*
219 ** data[i].id, data[i].hits, data[i].diagnostic, data[i].records, data[i].state
220 */
221 function my_onbytarget(data){}
222
223 /*
224 *********************************************************************************
225 ** HELPER FUNCTIONS *************************************************************
226 *********************************************************************************
227 */
228 function fireSearch()
229 {
230     $('div.showing').empty().text('No records to show.');
231     $('div.pages').empty().html('&nbsp;');
232     $('div.records').empty();
233     if( !curQuery.totalLength() )
234         return false;
235     my_paz.search(curQuery.toCCL(), currentResultsPerPage, currentSort, curQuery.getFilterString() );
236 }
237
238 function toggleAdvanced()
239 {
240     if(advancedOn){
241         $("div.advanced").hide();
242         $("div.search").height(73);
243         advancedOn = false;
244         $("#advanced").text("Advanced search");
245     } else {
246         $("div.search").height(173);
247         $("div.advanced").show();
248         advancedOn = true;
249         $("#advanced").text("Simple search");
250         loadFormFieldsFromQuery();
251     }
252 }
253
254 function drawDetailedRec(detailBox)
255 {
256     if( detailBox == undefined )
257         detailBox = $('<div class="detail"></div>').appendTo($('#rec_'+currentDetailedId));
258     
259     detailBox.append('Details:<hr/>');
260     var detailTable = $('<table></table>');
261     var recDate = currentDetailedData["md-date"];
262     var recSubject = currentDetailedData["md-subject"];
263     var recLocation = currentDetailedData["location"];
264
265     if( recDate )
266         detailTable.append('<tr><td class="item">Published:</td><td>'+recDate+'</td></tr>');
267     if( recSubject )
268         detailTable.append('<tr><td class="item">Subject:</td><td>'+recSubject+'</td></tr>');
269     if( recLocation )
270         detailTable.append('<tr><td class="item">Available at:</td><td>&nbsp;</td></tr>');
271
272     for(var i=0; i < recLocation.length; i++)
273     {
274         detailTable.append('<tr><td class="item">&nbsp;</td><td>'+recLocation[i].name+'</td></tr>');
275     }
276
277     detailTable.appendTo(detailBox);
278 }
279
280 function refine(field, value, opt)
281 {
282     switch(field) {
283         case "author":  curQuery.addTerm('au', value); break;
284         case "title":   curQuery.addTerm('ti', value); break;
285         case "date":    curQuery.addTerm('date', value); break;
286         case "subject": curQuery.addTerm('su', value); break;
287         case "xtarget": curQuery.setFilter(opt, value); break;
288     }
289
290     if(advancedOn)
291         loadFormFieldsFromQuery();
292
293     currentPage = 0;
294     drawBreadcrumb();
295     fireSearch();
296 }
297
298 function loadQueryFromForm()
299 {
300     curQuery.reset();
301     curQuery.simpleQuery = document.search.query.value;
302
303     if( advancedOn )
304     {
305         curQuery.addTermsFromList(document.search.author.value, 'au');
306         curQuery.addTermsFromList(document.search.title.value, 'ti');
307         curQuery.addTermsFromList(document.search.date.value, 'date');
308         curQuery.addTermsFromList(document.search.subject.value, 'su');
309     }
310 }
311
312 function loadFormFieldsFromQuery()
313 {
314     document.search.author.value = '';
315     document.search.title.value = '';
316     document.search.date.value = '';
317     document.search.subject.value = '';
318
319     for(var i = 0; i < curQuery.numTerms; i++)
320     {
321         switch( curQuery.getTermFieldByIdx(i) )
322         {
323             case "au": document.search.author.value += curQuery.getTermValueByIdx(i) + ';'; break;
324             case "ti": document.search.title.value += curQuery.getTermValueByIdx(i) + ';'; break;
325             case "date": document.search.date.value += curQuery.getTermValueByIdx(i) + ';'; break;
326             case "su": document.search.subject.value += curQuery.getTermValueByIdx(i) + ';'; break;
327         }
328     }
329 }
330
331 function drawPager(max, hits)
332 {
333     var firstOnPage = currentPage * currentResultsPerPage + 1;
334     var lastOnPage = (firstOnPage + currentResultsPerPage - 1) < max ? (firstOnPage + currentResultsPerPage - 1) : max;
335
336     var results = $('div.showing');
337     results.empty();
338     results.append('Displaying: <b>'+firstOnPage+'</b> to <b>'+lastOnPage+
339                             '</b> of <b>'+max+'</b> (total hits: '+hits+')');
340     var pager = $('div.pages');
341     pager.empty();
342     
343     if ( currentPage > 0 ){
344         $('<a class="previous_active">Previous</a>').click(function() { my_paz.showPrev(1); currentPage--; }).appendTo(pager.eq(0));
345         $('<a class="previous_active">Previous</a>').click(function() { my_paz.showPrev(1); currentPage--; }).appendTo(pager.eq(1));
346     }
347     else
348         pager.append('<a class="previous_inactive">Previous</a>');
349
350     var numPages = Math.ceil(max / currentResultsPerPage);
351
352     var start = ( currentPage - 5 > 0 ? currentPage - 5 : 1 );
353     var stop =  ( start + 12 < numPages ? start + 12 : numPages );
354
355     if (start > 1) $('<span>... </span>').appendTo(pager);
356     
357     for(var i = start; i <= stop; i++)
358     {
359         if( i == (currentPage + 1) ){
360            $('<a class="select">'+i+'</a>').appendTo(pager);
361            continue;
362         }
363         var pageLink = $('<a class="page">'+i+'</a>');
364         var plClone = pageLink.clone();
365
366         pageLink.click(function() { 
367             my_paz.showPage(this.firstChild.nodeValue - 1);
368             currentPage = (this.firstChild.nodeValue - 1);
369             });
370
371         plClone.click(function() { 
372             my_paz.showPage(this.firstChild.nodeValue - 1);
373             currentPage = (this.firstChild.nodeValue - 1);
374             });
375
376         //nasty hack
377         pager.eq(0).append(pageLink);
378         pager.eq(1).append(plClone);
379     }
380
381     if (stop < numPages) $('<span> ...</span>').appendTo(pager);
382
383     if ( currentPage < (numPages-1) ){
384         $('<a class="next_active">Next</a>').click(function() { my_paz.showNext(1); currentPage++; }).appendTo(pager.eq(0));
385         $('<a class="next_active">Next</a>').click(function() { my_paz.showNext(1); currentPage++; }).appendTo(pager.eq(1));
386     }
387     else
388         pager.append('<a class="next_inactive">Next</a>');
389 }
390
391 function drawBreadcrumb()
392 {
393     var bc = $("#breadcrumb");
394     bc.empty();
395     
396     if(curQuery.filterNums) $('<strong id="filter"><a>'+curQuery.getFilterName(0)+'</a>: </strong>').click(function() {
397                                 curQuery.removeFilter(0);
398                                 refine();
399                                 }).appendTo(bc);
400
401     bc.append('<span>'+curQuery.simpleQuery+'</span>');
402
403     for(var i = 0; i < curQuery.numTerms; i++){
404         bc.append('<strong> + </strong>');
405         var bcLink = $('<a id="pos_'+i+'">'+curQuery.getTermValueByIdx(i)+'</a>').click(function() { 
406                                             curQuery.removeTermByIdx(this.id.split('_')[1]);
407                                             refine(); 
408                                             });
409         bc.append(bcLink);
410     }
411 }