Towards a theme for generalised facet limits.
[mkdru-moved-to-drupal.org.git] / mkdru.client.js
1 // Set up namespace and some state.
2 var mkdru = {
3   // Settings to pass to pz2.js
4   useSessions: Drupal.settings.mkdru.use_sessions === '1',
5   // Variables
6   active: false,
7   pz2: null,
8   totalRec: 0,
9   pagerRange: 6,
10   pazpar2Path: Drupal.settings.mkdru.pz2_path,
11   // Facets
12   facets: {
13     source: {
14       pz2Name: 'xtargets',
15       max: Drupal.settings.mkdru.source_max
16     },
17     subject: {
18       pz2Name: 'subject',
19       max: Drupal.settings.mkdru.subject_max,
20       multiLimit: true,
21       limiter: 'su'
22     },
23     author: {
24       pz2Name: 'author',
25       max: Drupal.settings.mkdru.author_max,
26       multiLimit: false,
27       limiter: 'au'
28     }
29   },
30   // State
31   defaultState: {
32     page: 1,
33     perpage: 20,
34     sort: 'relevance',
35     filter: null,
36     query:'',
37     recid:null
38   },
39   state: {},
40   realm: ''
41 };
42
43
44
45 // Wrapper for jQuery
46 (function ($) {
47
48 // So we can use jQuery BBQ with Drupal 6 and its 1.2.6 jQuery
49 if (!$.isArray) $.isArray = function(obj) {
50   return Object.prototype.toString.call(obj) === "[object Array]";
51 };
52
53 // BBQ has no handy way to remove params without changing the hash.
54 // This takes an object to add and an array of keys to delete.
55 mkdru.hashAddDelMany = function (add, del) {
56   var newHash = $.deparam.fragment();
57   if (typeof(add) === 'object')
58     $.extend(newHash, add);
59   if ($.isArray(del))
60     for (var i=0; i < del.length; i++)
61       if (newHash[del[i]] !== 'undefined')
62         delete newHash[del[i]];
63   return $.param.fragment("#", newHash);
64 }
65
66 // It's sometimes cumbersome that object literals can't take variable keys.
67 mkdru.hashAddDelOne = function (key, value, del) {
68   var toAdd;
69   var toDel;
70   if (key && value) {
71     var toAdd = {};
72     toAdd[key] = value;
73   }
74   if (del) {
75     var toDel = [];
76     toDel.push(del);
77   }
78   return mkdru.hashAddDelMany(toAdd, toDel);
79 }
80
81
82
83 // pz2.js event handlers:
84 mkdru.pz2Init = function () {
85   if (mkdru.state.query) {
86     mkdru.search();
87   }
88   mkdru.pz2.stat();
89   //mkdru.pz2.bytarget();
90 };
91
92 mkdru.pz2Show = function (data) {
93   mkdru.totalRec = data.merged;
94   $('.mkdru-pager').html(mkdru.generatePager());
95   $('.mkdru-counts').html(Drupal.theme('mkdruCounts', data.start + 1,
96                                       data.num, data.merged, data.total));
97   var html = "";
98   for (var i = 0; i < data.hits.length; i++) {
99     html += Drupal.theme('mkdruResult', data.hits[i], 
100       i + 1 + mkdru.state.perpage * (mkdru.state.page - 1),
101       "#" + $.param.fragment($.param.fragment(
102         window.location.href, {recid: data.hits[i].recid})) + "\n"
103     );
104   }
105   $('.mkdru-result-list').html(html);
106   if (mkdru.state.recid) {
107     mkdru.pz2.record(mkdru.state.recid);
108   }
109   else {
110     $('.mkdru-results').show();
111   }
112 };
113
114 mkdru.pz2Status = function (data) {
115   $('.mkdru-status').html(Drupal.theme('mkdruStatus', data.activeclients, data.clients));
116 };
117
118 mkdru.pz2Term = function (data) {
119   for (var facet in mkdru.facets) {
120     // facet is limited
121     if (mkdru.state['limit_' + facet]) {
122       $('.mkdru-facet-' + facet).html(Drupal.theme('mkdruFacetLimit',
123                                  data[mkdru.facets[facet].pz2Name][0],
124                                  mkdru.hashAddDelOne(null, null, 'limit_' + facet)));
125     } else {
126       $('.mkdru-facet-' + facet).html(Drupal.theme('mkdruFacet',
127                                  data[mkdru.facets[facet].pz2Name],
128                                  facet, mkdru.facets[facet].max));
129     }
130   }
131 };
132
133 mkdru.pz2ByTarget = function (data) {
134   
135 };
136
137 mkdru.pz2Record = function (data) {
138   clearTimeout(mkdru.pz2.showTimer);
139   $('.mkdru-results').hide();
140   $('.mkdru-detail').html(Drupal.theme('mkdruDetail', data,
141                                        mkdru.hashAddDelOne(null, null, 'recid')));
142   $('.mkdru-detail').show();
143   clearTimeout(mkdru.pz2.recordTimer);
144 };
145
146
147
148 // State and URL handling 
149
150 // populate state from an object and fill in the blanks with defaults
151 mkdru.stateFromObject = function (obj) {
152   mkdru.state = $.extend({}, mkdru.defaultState);
153   for (var key in mkdru.defaultState)
154     if (typeof(obj[key]) != "undefined")
155       mkdru.state[key] = obj[key];
156 };
157
158 // populate state from current window's hash string
159 mkdru.stateFromHash = function () {
160   mkdru.stateFromObject($.deparam.fragment());
161 };
162
163 // set current window's hash string from state
164 mkdru.hashFromState = function () {
165   // only include non-default settings in the URL
166   var alteredState = {};
167   for (var key in mkdru.defaultState) {
168     if (mkdru.state[key] != mkdru.defaultState[key]) {
169       alteredState[key] = mkdru.state[key];
170     }
171   }
172   $.bbq.pushState(alteredState, 2);
173 };
174
175 // update mkdru_form theme's ui to match state
176 mkdru.uiFromState = function () {
177   for (var key in mkdru.state) {
178     switch(key) {
179     case 'query':
180       $('.mkdru-search input:text').attr('value', mkdru.state[key]);
181       break;
182     case 'perpage':
183       $('.mkdru-perpage').attr('value', mkdru.state[key]);
184       break;
185     case 'sort':
186       $('.mkdru-sort').attr('value', mkdru.state[key]);
187       break;
188     }
189   }
190 };
191
192 mkdru.hashChange = function () {
193   mkdru.stateFromHash();
194   // Request for details
195   if (mkdru.state.recid) {
196     mkdru.pz2.record(mkdru.state.recid);
197   }
198   // Other internal link
199   else {
200     // may need to run search again to limit targets
201     for (key in mkdru.state) {
202       if (key.substring(0,5) === 'limit' && mkdru.state[key]) {
203         mkdru.search();
204         break;
205       }
206     }
207     mkdru.pz2.showPage(mkdru.state.page-1);
208     $('.mkdru-detail').hide();
209     $('.mkdru-results').show();
210   }
211 };
212
213 // return link to limit facet
214 mkdru.addLimit = function (facet, limit) {
215   var newHash = $.deparam.fragment();
216   delete newHash['page'];
217   if ((typeof(newHash['limit_' + facet]) === 'undefined')
218        || !mkdru.facets[facet].multiLimit) {
219     newHash['limit_' + facet] = limit;
220   }
221   else {
222     newHash['limit_' + facet] += ';' + limit;
223   }
224   return $.param.fragment("#", newHash);
225 };
226
227 // return link to remove limit from facet
228 mkdru.removeLimit = function (facet, limit) {
229   var newHash = $.deparam.fragment();
230   delete newHash['page'];
231   if (!newHash['limit_' + facet].indexOf(';') || !mkdru.facets[facet].multiLimit) { 
232     delete newHash['limit_' + facet];
233   }
234   else {
235     var limits = newHash['limit_' + facet].split(';');
236     for (var i = 0; i < limits.length; i++) {
237       if (limits[i] == limit) {
238         limits.splice(i, 1);
239         newHash['limit_' + facet] = limits.join(';');
240         break;
241       }
242     }
243   }
244   return $.param.fragment("#", newHash);
245 };
246
247
248
249 // form submit handler
250 mkdru.submitQuery = function () {
251   // new query, back to defaults (shallow copy)
252   mkdru.state = $.extend({}, mkdru.defaultState);
253   mkdru.state.query = $('.mkdru-search input:text').attr('value');
254   mkdru.pollDropDowns();
255   mkdru.hashFromState();
256   mkdru.search();
257   mkdru.active = true;
258   return false;
259 };
260
261 // criteria drop-down (perpage, sort) handler
262 mkdru.submitCriteria = function () {
263   mkdru.pollDropDowns();
264   //search is not ON, do nothing
265   if (!mkdru.active) return false;
266   // pages mean different things now
267   mkdru.state.page = 1;
268   mkdru.hashFromState();
269   mkdru.pz2.show(0, mkdru.state.perpage, mkdru.state.sort);
270   return false;
271 }
272
273 mkdru.search = function () {
274   var filter = null;
275   var query = mkdru.state.query;
276
277   // facet limit implementation
278   for (var facet in mkdru.facets) {
279     // facet is limited
280     if (mkdru.state['limit_' + facet]) {
281       if (facet == "source") {
282         filter = 'pz:id=' + mkdru.state.limit_source;
283       }
284       else {
285         var limits = mkdru.state['limit_' + facet].split(';');
286         for (var i = 0; i < limits.length; i++) {
287           // ex. query + and au="{limit_author}"
288           query += ' and ' + mkdru.facets[facet]['limiter'] + '="'
289                   + mkdru.state['limit_' + facet] + '"';
290         }
291       }
292     }
293   }
294
295   mkdru.pz2.search(query, mkdru.state.perpage, mkdru.state.sort, filter);
296   mkdru.active = true;
297 };
298
299 mkdru.pollDropDowns = function () {
300   mkdru.state.perpage = $('.mkdru-perpage').attr('value');
301   mkdru.state.sort = $('.mkdru-sort').attr('value');
302 };
303
304 mkdru.generatePager = function () {
305   // cast page parameter to numeric so we can add to it
306   if (typeof mkdru.state.page == "string") {
307     mkdru.state.page = Number(mkdru.state.page);
308   }
309   var total = Math.ceil(mkdru.totalRec / mkdru.state.perpage);
310   var first = (mkdru.state.page - mkdru.pagerRange > 0)
311       ? mkdru.state.page - mkdru.pagerRange : 1;
312   var last = first + 2 * mkdru.pagerRange < total
313       ? first + 2 * mkdru.pagerRange : total;
314   var prev = null;
315   var next = null;
316   var pages = [];
317
318   if ((mkdru.state.page - 1) >= first) {
319     prev = "#" + $.param.fragment($.param.fragment(
320                window.location.href, {page: mkdru.state.page - 1}))
321   }
322   if ((mkdru.state.page + 1) <= total) {
323     next = "#" + $.param.fragment($.param.fragment(
324                window.location.href, {page: mkdru.state.page + 1}))
325   }
326
327   for (var i = first; i <= last; i++) {
328     pages.push("#" + $.param.fragment($.param.fragment(
329                window.location.href, {page: i})));
330   }
331
332   return Drupal.theme('mkdruPager', pages, first, mkdru.state.page,
333                       total, prev, next);
334 };
335
336
337
338 // wait until the DOM is ready, bind events
339 // and instantiate pz2 library
340 $(document).ready(function () {
341   $(window).bind( 'hashchange', mkdru.hashChange);
342   $('.mkdru-search').bind('submit', mkdru.submitQuery);
343   $('.mkdru-search input:text').attr('value', '');
344   $('.mkdru-perpage').bind('change', mkdru.submitCriteria);
345   $('.mkdru-sort').bind('change', mkdru.submitCriteria);
346
347   // generate termlist for pz2.js and populate facet limit state
348   var termlist = [];
349   for (var key in mkdru.facets) {
350     termlist.push(mkdru.facets[key].pz2Name);
351     mkdru.defaultState['limit_' + key] = null;
352   }
353
354   mkdru.pz2 = new pz2( { "onshow": mkdru.pz2Show,
355               "showtime": 500, //each timer (show, stat, term, bytarget) can be specified this way
356               "pazpar2path": mkdru.pazpar2Path,
357               "oninit": mkdru.pz2Init,
358               "onstat": mkdru.pz2Status,
359               "onterm": mkdru.pz2Term,
360               "termlist": termlist.join(','),
361               "onbytarget": mkdru.pz2ByTarget,
362               "usesessions" : mkdru.useSessions,
363               "showResponseType": mkdru.showResponseType,
364               "onrecord": mkdru.pz2Record,
365               "autoInit": false } );
366
367   // initialise state to hash string or defaults
368   mkdru.stateFromHash();
369   // and update UI to match
370   mkdru.uiFromState();
371
372   // ting thing
373   if (typeof(Drupal.settings.mkdru.query) !== "undefined") {
374     mkdru.state.query = Drupal.settings.mkdru.query
375   }
376
377   //not running against SP? init, otherwise authenticate
378   if (mkdru.useSessions) {
379     mkdru.pz2.init();
380   } else {
381     //runnin against SP
382     var user = Drupal.settings.mkdru.sp_user;
383     var pass = Drupal.settings.mkdru.sp_pass;
384     var params = {};
385     params['command'] = 'auth';
386     if (user && pass) {
387       params['action'] = 'login';
388       params['username'] = user;
389       params['password'] = pass;
390     } else {
391       params['action'] = 'ipauth';
392     }
393     var authReq = new pzHttpRequest(mkdru.pazpar2Path, 
394       function (err) {
395         alert("Authentication against metasearch gateway failed: " +err);
396       }
397     );
398     authReq.get(params,
399       function (data) {
400         var s = data.getElementsByTagName('status');
401         if (s.length && Element_getTextContent(s[0]) == "OK") {
402           mkdru.realm = data.getElementsByTagName('realm');
403           mkdru.pz2Init();
404         } else {
405           alert("Malformed response when authenticating against the metasearch"
406             + " gateway");
407         }
408       }
409     );
410   }
411 });
412 })(jQuery);