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