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