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