Add files from release 1.0.0
[mkws-moved-to-github.git] / tools / htdocs / releases / mkws-1.0.0.js
1 // Handlebars helpers
2
3 Handlebars.registerHelper('mkws-json', function(obj) {
4   return mkws.$.toJSON(obj);
5 });
6
7
8 // This is intended to handle paragraphs from Wikipedia, hence the
9 // rather hacky code to remove numbered references.
10 //
11 Handlebars.registerHelper('mkws-paragraphs', function(obj, nPara, nSent) {
12   var acc = [];
13
14   // For some reason, Handlebars provides the value
15   // {"hash":{},"data":{}} for parameters that are not provided. So we
16   // have to be prepared for actual numbers, explicitly undefined
17   // values and this dumb magic value.
18   if (obj && (nPara === undefined || nPara.hasOwnProperty('hash') || nPara == 0 || nPara > obj.length)) {
19     nPara = obj.length;
20   }
21   if (nSent === undefined || nSent.hasOwnProperty('hash') || nSent == 0) {
22     nSent = Infinity;
23   }
24
25   for (var i = 0; i < nPara; i++) {
26     // Remove numbered references such as "[1,3,4]" from text
27     var text = obj[i].replace(/\[[0-9,]+\]/g, '');
28     // Next line from http://stackoverflow.com/questions/18914629/split-string-into-sentences-in-javascript
29     var sentences = text.replace(/([.?!])\s*(?=[A-Z])/g, "$1|").split("|");
30     if (sentences.length > nSent)
31       sentences.length = nSent;
32
33     acc.push('<p>', sentences.join(' '), '</p>');
34     nSent -= sentences.length;
35     if (nSent == 0)
36       break;
37   }
38
39   return acc.join('');
40 });
41
42
43 Handlebars.registerHelper('mkws-translate', function(s) {
44   return mkws.M(s);
45 });
46
47
48 // We need {{mkws-attr '@name'}} because Handlebars can't parse {{@name}}
49 Handlebars.registerHelper('mkws-attr', function(attrName) {
50   return this[attrName];
51 });
52
53
54 /*
55  * Use as follows: {{#mkws-if-any NAME1 having="NAME2"}}
56  * Applicable when NAME1 is the name of an array
57  * The guarded code runs only if at least one element of the NAME1
58  * array has a subelement called NAME2.
59  */
60 Handlebars.registerHelper('mkws-if-any', function(items, options) {
61   var having = options.hash.having;
62   for (var i in items) {
63     var item = items[i]
64     if (!having || item[having]) {
65       return options.fn(this);
66     }
67   }
68   return "";
69 });
70
71
72 Handlebars.registerHelper('mkws-first', function(items, options) {
73   var having = options.hash.having;
74   for (var i in items) {
75     var item = items[i]
76     if (!having || item[having]) {
77       return options.fn(item);
78     }
79   }
80   return "";
81 });
82
83
84 Handlebars.registerHelper('mkws-commaList', function(items, options) {
85   var out = "";
86
87   for (var i in items) {
88     if (i > 0) out += ", ";
89     out += options.fn(items[i])
90   }
91
92   return out;
93 });
94
95
96 Handlebars.registerHelper('mkws-index1', function(obj) {
97   return obj.data.index + 1;
98 });
99
100 Handlebars.registerHelper('mkws-repeat', function(count, options) {
101   var out = "";
102   for (var i = 0; i < count; i++) {
103     out += options.fn(this);
104   }
105   return out;
106 });
107 /*! MKWS, the MasterKey Widget Set.
108  *  Copyright (C) 2013-2014 Index Data
109  *  See the file LICENSE for details
110  */
111
112 "use strict"; // HTML5: disable for log_level >= 2
113
114
115 // Set up global mkws object. Contains truly global state such as SP
116 // authentication, and a hash of team objects, indexed by team-name.
117 //
118 // We set it as a property of window to make the global explicit as
119 // some things complain about an implicit global.
120 window.mkws = {
121   $: $, // Our own local copy of the jQuery object
122   authenticated: false,
123   authenticating: false,
124   active: false,
125   log_level: 1, // Will be overridden from mkws.config, but
126                 // initial value allows jQuery popup to use logging.
127   teams: {},
128   widgetType2function: {},
129   defaultTemplates: {},
130
131   locale_lang: {
132     "de": {
133       "Authors": "Autoren",
134       "Subjects": "Schlagw&ouml;rter",
135       "Sources": "Daten und Quellen",
136       "source": "datenquelle",
137       "Facets": "Termlisten",
138       "Next": "Weiter",
139       "Prev": "Zur&uuml;ck",
140       "Search": "Suche",
141       "Sort by": "Sortieren nach",
142       "and show": "und zeige",
143       "per page": "pro Seite",
144       "Displaying": "Zeige",
145       "to": "von",
146       "of": "aus",
147       "found": "gefunden",
148       "Title": "Titel",
149       "Author": "Autor",
150       "author": "autor",
151       "Date": "Datum",
152       "Subject": "Schlagwort",
153       "subject": "schlagwort",
154       "Location": "Ort",
155       "Records": "Datens&auml;tze",
156       "Targets": "Datenbanken",
157
158       "dummy": "dummy"
159     },
160
161     "da": {
162       "Authors": "Forfattere",
163       "Subjects": "Emner",
164       "Sources": "Kilder",
165       "source": "kilder",
166       "Facets": "Termlists",
167       "Next": "N&aelig;ste",
168       "Prev": "Forrige",
169       "Search": "S&oslash;g",
170       "Sort by": "Sorter efter",
171       "and show": "og vis",
172       "per page": "per side",
173       "Displaying": "Viser",
174       "to": "til",
175       "of": "ud af",
176       "found": "fandt",
177       "Title": "Title",
178       "Author": "Forfatter",
179       "author": "forfatter",
180       "Date": "Dato",
181       "Subject": "Emneord",
182       "subject": "emneord",
183       "Location": "Lokation",
184       "Records": "Poster",
185       "Targets": "Baser",
186
187       "dummy": "dummy"
188     }
189   }
190 };
191
192 // We may be using a separate copy
193 if (typeof(mkws_jQuery) !== "undefined") {
194   mkws.$ = mkws_jQuery;
195 } else {
196   mkws.$ = jQuery;
197 }
198
199 mkws.log = function(string) {
200   if (!mkws.log_level)
201     return;
202
203   if (typeof console === "undefined" || typeof console.log === "undefined") { /* ARGH!!! old IE */
204     return;
205   }
206
207   // you need to disable use strict at the top of the file!!!
208   if (mkws.log_level >= 3) {
209     // Works in Chrome; not sure about elsewhere
210     console.trace();
211   } else if (mkws.log_level >= 2) {
212     console.log(">>> called from function " + arguments.callee.caller.name + ' <<<');
213   }
214   console.log(string);
215 };
216
217
218 // Translation function.
219 mkws.M = function(word) {
220   var lang = mkws.config.lang;
221
222   if (!lang || !mkws.locale_lang[lang])
223     return word;
224
225   return mkws.locale_lang[lang][word] || word;
226 };
227
228
229 // This function is taken from a StackOverflow answer
230 // http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript/901144#901144
231 mkws.getParameterByName = function(name, url) {
232   if (!url) url = location.search;
233   name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
234   var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
235   results = regex.exec(url);
236   return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
237 }
238
239
240 mkws.registerWidgetType = function(name, fn) {
241   if(mkws._old2new.hasOwnProperty(name)) {
242       mkws.log("Warning: registerWidgetType old widget name: " + name + " => " + mkws._old2new[name]);
243       name = mkws._old2new[name];
244   }
245
246   mkws.widgetType2function[name] = fn;
247   mkws.log("registered widget-type '" + name + "'");
248 };
249
250 mkws.promotionFunction = function(name) {
251   return mkws.widgetType2function[name];
252 };
253
254
255 mkws.setMkwsConfig = function(overrides) {
256   // Set global log_level flag early so that mkws.log() works
257   var tmp = overrides.log_level;
258   if (typeof(tmp) !== 'undefined') mkws.log_level = tmp;
259
260   var config_default = {
261     use_service_proxy: true,
262     pazpar2_url: undefined,
263     pp2_hostname: "sp-mkws.indexdata.com",
264     pp2_path: "service-proxy/",
265     service_proxy_auth: undefined,
266     sp_auth_path: undefined,
267     sp_auth_query: "command=auth&action=perconfig",
268     sp_auth_credentials: undefined,
269     lang: "",
270     sort_options: [["relevance"], ["title:1", "title"], ["date:0", "newest"], ["date:1", "oldest"]],
271     perpage_options: [10, 20, 30, 50],
272     sort_default: "relevance",
273     perpage_default: 20,
274     show_lang: true,    /* show/hide language menu */
275     show_sort: true,    /* show/hide sort menu */
276     show_perpage: true, /* show/hide perpage menu */
277     show_switch: true,  /* show/hide switch menu */
278     lang_options: [],   /* display languages links for given languages, [] for all */
279     facets: ["xtargets", "subject", "author"], /* display facets, in this order, [] for none */
280     responsive_design_width: undefined, /* a page with less pixel width considered as narrow */
281     log_level: 1,     /* log level for development: 0..2 */
282     template_vars: {}, /* values that may be exposed to templates */
283
284     dummy: "dummy"
285   };
286
287   mkws.config = mkws.objectInheritingFrom(config_default);
288   for (var k in overrides) {
289     mkws.config[k] = overrides[k];
290   }
291 };
292
293
294 // This code is from Douglas Crockford's article "Prototypal Inheritance in JavaScript"
295 // http://javascript.crockford.com/prototypal.html
296 // mkws.objectInheritingFrom behaves the same as Object.create,
297 // but since the latter is not available in IE8 we can't use it.
298 //
299 mkws.objectInheritingFrom = function(o) {
300   function F() {}
301   F.prototype = o;
302   return new F();
303 }
304
305
306 // The following functions are dispatchers for team methods that
307 // are called from the UI using a team-name rather than implicit
308 // context.
309 mkws.switchView = function(tname, view) {
310   mkws.teams[tname].switchView(view);
311 };
312
313 mkws.showDetails = function(tname, prefixRecId) {
314   mkws.teams[tname].showDetails(prefixRecId);
315 };
316
317 mkws.limitTarget  = function(tname, id, name) {
318   mkws.teams[tname].limitTarget(id, name);
319 };
320
321 mkws.limitQuery  = function(tname, field, value) {
322   mkws.teams[tname].limitQuery(field, value);
323 };
324
325 mkws.limitCategory  = function(tname, id) {
326   mkws.teams[tname].limitCategory(id);
327 };
328
329 mkws.delimitTarget = function(tname, id) {
330   mkws.teams[tname].delimitTarget(id);
331 };
332
333 mkws.delimitQuery = function(tname, field, value) {
334   mkws.teams[tname].delimitQuery(field, value);
335 };
336
337 mkws.showPage = function(tname, pageNum) {
338   mkws.teams[tname].showPage(pageNum);
339 };
340
341 mkws.pagerPrev = function(tname) {
342   mkws.teams[tname].pagerPrev();
343 };
344
345 mkws.pagerNext = function(tname) {
346   mkws.teams[tname].pagerNext();
347 };
348
349
350 mkws.pazpar2_url = function() {
351   if (mkws.config.pazpar2_url) {
352     mkws.log("using pre-baked pazpar2_url '" + mkws.config.pazpar2_url + "'");
353     return mkws.config.pazpar2_url;
354   } else {
355     var s = document.location.protocol + "//" + mkws.config.pp2_hostname + "/" + mkws.config.pp2_path;
356     mkws.log("generated pazpar2_url '" + s + "'");
357     return s;
358   }
359 };
360
361
362 // We put a session token in window.name, as it's the only place to
363 // keep data that is preserved across reloads and within-site
364 // navigation. pz2.js picks this up and uses it as part of the
365 // cookie-name, to ensure we get a new session when we need one.
366 //
367 // We want to use different sessions for different windows/tabs (so
368 // they don't receive each other's messages), different hosts and
369 // different paths on a host (since in general these will
370 // authenticate as different libraries). So the window name needs to
371 // include the hostname and the path from the URL, plus the token.
372 //
373 var token;
374 if (window.name) {
375   token = window.name.replace(/.*\//, '');
376   mkws.log("Reusing existing window token '" + token + "'");
377 } else {
378   // Incredible that the standard JavaScript runtime doesn't define a
379   // unique windowId. Instead, we have to make one up. And since there's
380   // no global area shared between windows, the best we can do for
381   // ensuring uniqueness is generating a random ID and crossing our
382   // fingers.
383   //
384   // Ten chars from 26 alpha-numerics = 36^10 = 3.65e15 combinations.
385   // At one per second, it will take 116 million years to duplicate a token
386   token = Math.random().toString(36).slice(2, 12);
387   mkws.log("Generated new window token '" + token + "'");
388 }
389
390 window.name = window.location.hostname + window.location.pathname + '/' + token;
391 mkws.log("Using window.name '" + window.name + "'");
392
393
394 // wrapper to provide local copy of the jQuery object.
395 (function($) {
396   var log = mkws.log;
397   var _old2new = { // Maps old-style widget names to new-style
398     'Authname': 'auth-name',
399     'ConsoleBuilder': 'console-builder',
400     'Coverart': 'cover-art',
401     'GoogleImage': 'google-image',
402     'MOTD': 'motd',
403     'MOTDContainer': 'motd-container',
404     'Perpage': 'per-page',
405     'SearchForm': 'search-form',
406     'ReferenceUniverse': 'reference-universe',
407     'Termlists': 'facets'
408   };
409   // Annoyingly, there is no built-in way to invert a hash
410   var _new2old = {};
411   for (var key in _old2new) {
412     if(_old2new.hasOwnProperty(key)) {
413       _new2old[_old2new[key]] = key;
414     }
415   }
416
417   mkws._old2new = _old2new;
418
419   function handleNodeWithTeam(node, callback) {
420     // First branch for DOM objects; second branch for jQuery objects
421     var classes = node.className || node.attr('class');
422     if (!classes) {
423       // For some reason, if we try to proceed when classes is
424       // undefined, we don't get an error message, but this
425       // function and its callers, up several stack level,
426       // silently return. What a crock.
427       log("handleNodeWithTeam() called on node with no classes");
428       return;
429     }
430     var list = classes.split(/\s+/)
431     var teamName, type;
432
433     for (var i = 0; i < list.length; i++) {
434       var cname = list[i];
435       if (cname.match(/^mkws-team-/)) {
436         // New-style teamnames of the form mkws-team-xyz
437         teamName = cname.replace(/^mkws-team-/, '');
438       } else if (cname.match(/^mkwsTeam_/)) {
439         // Old-style teamnames of the form mkwsTeam_xyz
440         teamName = cname.replace(/^mkwsTeam_/, '');
441       } else if (cname.match(/^mkws-/)) {
442         // New-style names of the from mkws-foo-bar
443         type = cname.replace(/^mkws-/, '');
444       } else if (cname.match(/^mkws/)) {
445         // Old-style names of the form mkwsFooBar
446         var tmp = cname.replace(/^mkws/, '');
447         type = _old2new[tmp] || tmp.toLowerCase();
448       }
449     }
450
451     // Widgets without a team are on team "AUTO"
452     if (!teamName) {
453       teamName = "AUTO";
454       // Autosearch widgets don't join team AUTO if there is already an
455       // autosearch on the team or the team has otherwise gotten a query
456       if (node.getAttribute("autosearch")) {
457         if (mkws.autoHasAuto ||
458             mkws.teams["AUTO"] && mkws.teams["AUTO"].config["query"]) {
459           log("AUTO team already has a query, using unique team");
460           teamName = "UNIQUE";
461         }
462         mkws.autoHasAuto = true;
463       }
464     }
465
466     // Widgets on team "UNIQUE" get a random team
467     if (teamName === "UNIQUE") {
468       teamName = Math.floor(Math.random() * 100000000).toString();
469     }
470
471     callback.call(node, teamName, type);
472   }
473
474
475   function resizePage() {
476     var threshhold = mkws.config.responsive_design_width;
477     var width = $(window).width();
478     var from, to, method;
479
480     if ((mkws.width === undefined || mkws.width > threshhold) &&
481         width <= threshhold) {
482       from = "wide"; to = "narrow"; method = "hide";
483     } else if ((mkws.width === undefined || mkws.width <= threshhold) &&
484                width > threshhold) {
485       from = "narrow"; to = "wide"; method = "show";
486     }
487     mkws.width = width;
488
489     if (from) {
490       log("changing from " + from + " to " + to + ": " + width);
491       for (var tname in mkws.teams) {
492         var team = mkws.teams[tname];
493         team.visitWidgets(function(t, w) {
494           var w1 = team.widget(t + "-container-" + from);
495           var w2 = team.widget(t + "-container-" + to);
496           if (w1) {
497             w1.node.hide();
498           }
499           if (w2) {
500             w2.node.show();
501             w.node.appendTo(w2.node);
502           }
503         });
504         team.queue("resize-" + to).publish();
505       }
506     }
507   };
508
509
510   /*
511    * Run service-proxy authentication in background (after page load).
512    * The username/password is configured in the apache config file
513    * for the site.
514    */
515   function authenticateSession(auth_url, auth_domain, pp2_url) {
516     mkws.authenticating = true;
517     log("service proxy authentication on URL: " + auth_url);
518
519     if (!auth_domain) {
520       auth_domain = pp2_url.replace(/^(https?:)?\/\/(.*?)\/.*/, '$2');
521       log("guessed auth_domain '" + auth_domain + "' from pp2_url '" + pp2_url + "'");
522     }
523
524     var request = new pzHttpRequest(auth_url, function(err) {
525       alert("HTTP call for authentication failed: " + err)
526       return;
527     }, auth_domain);
528
529     request.get(null, function(data) {
530       mkws.authenticating = false;
531       if (!$.isXMLDoc(data)) {
532         alert("Service Proxy authentication response is not a valid XML document");
533         return;
534       }
535       var status = $(data).find("status");
536       if (status.text() != "OK") {
537         var message = $(data).find("message");
538         alert("Service Proxy authentication response: " + status.text() + " (" + message.text() + ")");
539         return;
540       }
541
542       log("service proxy authentication successful");
543       mkws.authenticated = true;
544       var authName = $(data).find("displayName").text();
545       // You'd think there would be a better way to do this:
546       var realm = $(data).find("realm:not(realmAttributes realm)").text();
547       for (var teamName in mkws.teams) {
548         mkws.teams[teamName].queue("authenticated").publish(authName, realm);
549       }
550
551       runAutoSearches();
552     });
553   }
554
555
556   function runAutoSearches() {
557     log("running auto searches");
558
559     for (var teamName in mkws.teams) {
560       mkws.teams[teamName].queue("ready").publish();
561     }
562   }
563
564
565   function selectorForAllWidgets() {
566     if (mkws.config && mkws.config.scan_all_nodes) {
567       // This is the old version, which works by telling jQuery to
568       // find every node that has a class beginning with "mkws". In
569       // theory it should be slower than the class-based selector; but
570       // instrumentation suprisingly shows this is consistently
571       // faster. It also has the advantage that any widgets of
572       // non-registered types are logged as warnings rather than
573       // silently ignored.
574       return '[class^="mkws"],[class*=" mkws"]';
575     } else {
576       // This is the new version, which works by looking up the
577       // specific classes of all registered widget types and their
578       // resize containers. Because all it requires jQuery to do is
579       // some hash lookups in pre-built tables, it should be very
580       // fast; but it silently ignores widgets of unregistered types.
581       var s = "";
582       for (var type in mkws.widgetType2function) {
583         if (s) s += ',';
584         s += '.mkws-' + type;
585         s += ',.mkws-' + type + "-container-wide";
586         s += ',.mkws-' + type + "-container-narrow";
587         // Annoyingly, we also need to recognise old-style names
588         var oldtype = _new2old[type] || type.charAt(0).toUpperCase() + type.slice(1);
589         s += ',.mkws' + oldtype;
590         s += ',.mkws' + oldtype + "-Container-wide";
591         s += ',.mkws' + oldtype + "-Container-narrow";
592       }
593       return s;
594     }
595   }
596
597
598   function makeWidgetsWithin(level, node) {
599     if (node) var widgetNodes = node.find(selectorForAllWidgets());
600     else widgetNodes = $(selectorForAllWidgets());
601     // Return false if we parse no widgets
602     if (widgetNodes.length < 1) return false;
603     widgetNodes.each(function() {
604       handleNodeWithTeam(this, function(tname, type) {
605         var myTeam = mkws.teams[tname];
606         if (!myTeam) {
607           myTeam = mkws.teams[tname] = mkws.makeTeam($, tname);
608         }
609
610         var oldHTML = this.innerHTML;
611         var myWidget = mkws.makeWidget($, myTeam, type, this);
612         myTeam.addWidget(myWidget);
613         var newHTML = this.innerHTML;
614         if (newHTML !== oldHTML) {
615           myTeam.log("widget " + type + " HTML changed: reparsing");
616           makeWidgetsWithin(level+1, $(this));
617         }
618       });
619     });
620     return true;
621   }
622
623
624   // The second "rootsel" parameter is passed to jQuery and is a DOM node
625   // or a selector string you would like to constrain the search for widgets to.
626   //
627   // This function has no side effects if run again on an operating session,
628   // even if the element/selector passed causes existing widgets to be reparsed: 
629   //
630   // (TODO: that last bit isn't true and we currently have to avoid reinitialising
631   // widgets, MKWS-261)
632   //
633   // * configuration is not regenerated
634   // * authentication is not performed again
635   // * autosearches are not re-run
636   mkws.init = function(message, rootsel) {
637     var greet = "MKWS initialised";
638     if (rootsel) greet += " (limited to " + rootsel + ")"
639     if (message) greet += " :: " + message; 
640     mkws.log(greet);
641
642     // MKWS is not active until init() has been run against an object with widget nodes.
643     // We only set initial configuration when MKWS is first activated.
644     if (!mkws.isActive) {
645       var widgetSelector = selectorForAllWidgets();
646       if ($(widgetSelector).length < 1) {
647         mkws.log("no widgets found");
648         return;
649       }
650
651       // Initial configuration
652       mkws.autoHasAuto = false;
653       var saved_config;
654       if (typeof mkws_config === 'undefined') {
655         log("setting empty config");
656         saved_config = {};
657       } else {
658         log("using config: " + $.toJSON(mkws_config));
659         saved_config = mkws_config;
660       }
661       mkws.setMkwsConfig(saved_config);
662
663       for (var key in mkws.config) {
664         if (mkws.config.hasOwnProperty(key)) {
665           if (key.match(/^language_/)) {
666             var lang = key.replace(/^language_/, "");
667             // Copy custom languages into list
668             mkws.locale_lang[lang] = mkws.config[key];
669             log("added locally configured language '" + lang + "'");
670           }
671         }
672       }
673
674       var lang = mkws.getParameterByName("lang") || mkws.config.lang;
675       if (!lang || !mkws.locale_lang[lang]) {
676         mkws.config.lang = ""
677       } else {
678         mkws.config.lang = lang;
679       }
680
681       log("using language: " + (mkws.config.lang ? mkws.config.lang : "none"));
682
683       // protocol independent link for pazpar2: "//mkws/sp" -> "https://mkws/sp"
684       if (mkws.pazpar2_url().match(/^\/\//)) {
685         mkws.config.pazpar2_url = document.location.protocol + mkws.config.pazpar2_url;
686         log("adjusted protocol independent link to " + mkws.pazpar2_url());
687       }
688
689       if (mkws.config.responsive_design_width) {
690         // Responsive web design - change layout on the fly based on
691         // current screen width. Required for mobile devices.
692         $(window).resize(resizePage);
693         // initial check after page load
694         $(document).ready(resizePage);
695       }
696     }
697
698     var then = $.now();
699     // If we've made no widgets, return without starting an SP session
700     // or marking MKWS active.
701     if (makeWidgetsWithin(1, rootsel ? $(rootsel) : undefined) === false) {
702       return false;
703     }
704     var now = $.now();
705
706     log("walking MKWS nodes took " + (now-then) + " ms");
707     for (var tName in mkws.teams) {
708       var myTeam = mkws.teams[tName]
709       myTeam.makePz2();
710       myTeam.log("made PZ2 object");
711       /*
712         myTeam.visitWidgets(function(t, w) {
713           log("  has widget of type '" + t + "': " + w);
714         });
715       */
716     }
717
718     function sp_auth_url(config) {
719       if (config.service_proxy_auth) {
720         mkws.log("using pre-baked sp_auth_url '" + config.service_proxy_auth + "'");
721         return config.service_proxy_auth;
722       } else {
723         var s = '//';
724         s += config.sp_auth_hostname ? config.sp_auth_hostname : config.pp2_hostname;
725         s += '/';
726         s += config.sp_auth_path ? config.sp_auth_path : config.pp2_path;
727         var q = config.sp_auth_query;
728         if (q) {
729           s += '?' + q;
730         }
731         var c = config.sp_auth_credentials;
732         if (c) {
733           s += ('&username=' + c.substr(0, c.indexOf('/')) +
734                 '&password=' + c.substr(c.indexOf('/')+1));
735         }
736         mkws.log("generated sp_auth_url '" + s + "'");
737         return s;
738       }
739     }
740
741     if (mkws.config.use_service_proxy && !mkws.authenticated && !mkws.authenticating) {
742       authenticateSession(sp_auth_url(mkws.config),
743                           mkws.config.service_proxy_auth_domain,
744                           mkws.pazpar2_url());
745     } else if (!mkws.authenticating) {
746       // raw pp2 or we have a session already open
747       runAutoSearches();
748     }
749     
750     mkws.isActive = true;
751     return true;
752   };
753
754   $(document).ready(function() {
755     if (!window.mkws_noready && !mkws.authenticating && !mkws.active) {
756        mkws.init();
757     }
758   });
759
760 })(mkws.$);
761 // Factory function for team objects. As much as possible, this uses
762 // only member variables (prefixed "m_") and inner functions with
763 // private scope.
764 //
765 // Some functions are visible as member-functions to be called from
766 // outside code -- specifically, from generated HTML. These functions
767 // are that.switchView(), showDetails(), limitTarget(), limitQuery(),
768 // limitCategory(), delimitTarget(), delimitQuery(), showPage(),
769 // pagerPrev(), pagerNext().
770 //
771 // Before the team can be used for searching and related operations,
772 // its pz2 object must be created by calling team.makePz2().
773 //
774 mkws.makeTeam = function($, teamName) {
775   var that = {};
776   var m_teamName = teamName;
777   var m_submitted = false;
778   var m_query; // initially undefined
779   var m_sortOrder; // will be set below
780   var m_perpage; // will be set below
781   var m_filterSet = filterSet(that);
782   var m_totalRecordCount = 0;
783   var m_currentPage = 1;
784   var m_currentRecordId = '';
785   var m_currentRecordData = null;
786   var m_logTime = {
787     // Timestamps for logging
788     "start": $.now(),
789     "last": $.now()
790   };
791   var m_paz; // will be initialised below
792   var m_templateText = {}; // widgets can register templates to be compiled
793   var m_template = {}; // compiled templates, from any source
794   var m_widgets = {}; // Maps widget-type to array of widget objects
795   var m_gotRecords = false;
796   
797   var config = mkws.objectInheritingFrom(mkws.config);
798   that.config = config;
799
800   that.toString = function() { return '[Team ' + teamName + ']'; };
801
802   // Accessor methods for individual widgets: readers
803   that.name = function() { return m_teamName; };
804   that.submitted = function() { return m_submitted; };
805   that.sortOrder = function() { return m_sortOrder; };
806   that.perpage = function() { return m_perpage; };
807   that.query = function() { return m_query; };
808   that.totalRecordCount = function() { return m_totalRecordCount; };
809   that.currentPage = function() { return m_currentPage; };
810   that.currentRecordId = function() { return m_currentRecordId; };
811   that.currentRecordData = function() { return m_currentRecordData; };
812   that.filters = function() { return m_filterSet; };
813   that.gotRecords = function() { return m_gotRecords; };
814
815   // Accessor methods for individual widgets: writers
816   that.set_sortOrder = function(val) { m_sortOrder = val };
817   that.set_perpage = function(val) { m_perpage = val };
818
819
820   // The following PubSub code is modified from the jQuery manual:
821   // http://api.jquery.com/jQuery.Callbacks/
822   //
823   // Use as:
824   //    team.queue("eventName").subscribe(function(param1, param2 ...) { ... });
825   //    team.queue("eventName").publish(arg1, arg2, ...);
826   //
827   var m_queues = {};
828   function queue(id) {
829     if (!m_queues[id]) {
830       var callbacks = $.Callbacks();
831       m_queues[id] = {
832         publish: callbacks.fire,
833         subscribe: callbacks.add,
834         unsubscribe: callbacks.remove
835       };
836     }
837     return m_queues[id];
838   };
839   that.queue = queue;
840
841
842   function log(s) {
843     var now = $.now();
844     var timestamp = (((now - m_logTime.start)/1000).toFixed(3) + " (+" +
845                      ((now - m_logTime.last)/1000).toFixed(3) + ") ");
846     m_logTime.last = now;
847     mkws.log(m_teamName + ": " + timestamp + s);
848     that.queue("log").publish(m_teamName, timestamp, s);
849   }
850   that.log = log;
851
852
853   log("making new widget team");
854
855   m_sortOrder = config.sort_default;
856   m_perpage = config.perpage_default;
857  
858   // pz2.js event handlers:
859   function onInit() {
860     log("init");
861     m_paz.stat();
862     m_paz.bytarget();
863   }
864
865   function onBytarget(data) {
866     log("bytarget");
867     queue("targets").publish(data);
868   }
869
870   function onStat(data) {
871     queue("stat").publish(data);
872     var hitcount = parseInt(data.hits[0], 10);
873     if (!m_gotRecords && hitcount > 0) {
874       m_gotRecords = true;
875       queue("firstrecords").publish(hitcount);
876     }
877     if (parseInt(data.activeclients[0], 10) === 0) {
878       log("complete");
879       queue("complete").publish(hitcount);
880     }
881   }
882
883   function onTerm(data) {
884     log("term");
885     queue("facets").publish(data);
886   }
887
888   function onShow(data, teamName) {
889     log("show");
890     m_totalRecordCount = data.merged;
891     log("found " + m_totalRecordCount + " records");
892     queue("pager").publish(data);
893     queue("records").publish(data);
894   }
895
896   function onRecord(data, args, teamName) {
897     log("record");
898     // FIXME: record is async!!
899     clearTimeout(m_paz.recordTimer);
900     queue("record").publish(data);
901     var detRecordDiv = findnode(recordDetailsId(data.recid[0]));
902     if (detRecordDiv.length) {
903       // in case on_show was faster to redraw element
904       return;
905     }
906     m_currentRecordData = data;
907     var recordDiv = findnode('.' + recordElementId(m_currentRecordData.recid[0]));
908     var html = renderDetails(m_currentRecordData);
909     $(recordDiv).append(html);
910   }
911
912
913   // create a parameters array and pass it to the pz2's constructor
914   // then register the form submit event with the pz2.search function
915   // autoInit is set to true on default
916   that.makePz2 = function() {
917     log("m_queues=" + $.toJSON(m_queues));
918     var params = {
919       "windowid": teamName,
920       "pazpar2path": mkws.pazpar2_url(),
921       "usesessions" : config.use_service_proxy ? false : true,
922       "showtime": 500,            //each timer (show, stat, term, bytarget) can be specified this way
923       "termlist": config.facets.join(',')
924     };
925
926     params.oninit = onInit;
927     if (m_queues.targets) {
928       params.onbytarget = onBytarget;
929       log("setting bytarget callback");
930     }
931     if (m_queues.stat) {
932       params.onstat = onStat;
933       log("setting stat callback");
934     }
935     if (m_queues.facets && config.facets.length) {
936       params.onterm = onTerm;
937       log("setting term callback");
938     }
939     if (m_queues.records) {
940       log("setting show callback");
941       params.onshow = onShow;
942       // Record callback is subscribed from records callback
943       log("setting record callback");
944       params.onrecord = onRecord;
945     }
946
947     m_paz = new pz2(params);
948     log("created main pz2 object");
949   }
950
951
952   // Used by the Records widget and onRecord()
953   function recordElementId(s) {
954     return 'mkws-rec_' + s.replace(/[^a-z0-9]/ig, '_');
955   }
956   that.recordElementId = recordElementId;
957
958   // Used by onRecord(), showDetails() and renderDetails()
959   function recordDetailsId(s) {
960     return 'mkws-det_' + s.replace(/[^a-z0-9]/ig, '_');
961   }
962
963
964   that.targetFiltered = function(id) {
965     return m_filterSet.targetFiltered(id);
966   };
967
968
969   that.limitTarget = function(id, name) {
970     log("limitTarget(id=" + id + ", name=" + name + ")");
971     m_filterSet.add(targetFilter(id, name));
972     if (m_query) triggerSearch();
973     return false;
974   };
975
976
977   that.limitQuery = function(field, value) {
978     log("limitQuery(field=" + field + ", value=" + value + ")");
979     m_filterSet.add(fieldFilter(field, value));
980     if (m_query) triggerSearch();
981     return false;
982   };
983
984
985   that.limitCategory = function(id) {
986     log("limitCategory(id=" + id + ")");
987     // Only one category filter at a time
988     m_filterSet.removeMatching(function(f) { return f.type === 'category' });
989     if (id !== '') m_filterSet.add(categoryFilter(id));
990     if (m_query) triggerSearch();
991     return false;
992   };
993
994
995   that.delimitTarget = function(id) {
996     log("delimitTarget(id=" + id + ")");
997     m_filterSet.removeMatching(function(f) { return f.type === 'target' });
998     if (m_query) triggerSearch();
999     return false;
1000   };
1001
1002
1003   that.delimitQuery = function(field, value) {
1004     log("delimitQuery(field=" + field + ", value=" + value + ")");
1005     m_filterSet.removeMatching(function(f) { return f.type == 'field' &&
1006                                              field == f.field && value == f.value });
1007     if (m_query) triggerSearch();
1008     return false;
1009   };
1010
1011
1012   that.showPage = function(pageNum) {
1013     m_currentPage = pageNum;
1014     m_paz.showPage(m_currentPage - 1);
1015   };
1016
1017
1018   that.pagerNext = function() {
1019     if (m_totalRecordCount - m_perpage*m_currentPage > 0) {
1020       m_paz.showNext();
1021       m_currentPage++;
1022     }
1023   };
1024
1025
1026   that.pagerPrev = function() {
1027     if (m_paz.showPrev() != false)
1028       m_currentPage--;
1029   };
1030
1031
1032   that.reShow = function() {
1033     resetPage();
1034     m_paz.show(0, m_perpage, m_sortOrder);
1035   };
1036
1037
1038   function resetPage() {
1039     m_currentPage = 1;
1040     m_totalRecordCount = 0;
1041     m_gotRecords = false;
1042   }
1043   that.resetPage = resetPage;
1044
1045
1046   function newSearch(query, sortOrder, maxrecs, perpage, limit, targets, torusquery) {
1047     log("newSearch: " + query);
1048
1049     if (config.use_service_proxy && !mkws.authenticated) {
1050       alert("searching before authentication");
1051       return;
1052     }
1053
1054     m_filterSet.removeMatching(function(f) { return f.type !== 'category' });
1055     triggerSearch(query, sortOrder, maxrecs, perpage, limit, targets, torusquery);
1056     switchView('records'); // In case it's configured to start off as hidden
1057     m_submitted = true;
1058   }
1059   that.newSearch = newSearch;
1060
1061
1062   function triggerSearch(query, sortOrder, maxrecs, perpage, limit, targets, torusquery) {
1063     resetPage();
1064     queue("navi").publish();
1065
1066     // Continue to use previous query/sort-order unless new ones are specified
1067     if (query) m_query = query;
1068     if (sortOrder) m_sortOrder = sortOrder;
1069     if (perpage) m_perpage = perpage;
1070     if (targets) m_filterSet.add(targetFilter(targets, targets));
1071
1072     var pp2filter = m_filterSet.pp2filter();
1073     var pp2limit = m_filterSet.pp2limit(limit);
1074     var pp2catLimit = m_filterSet.pp2catLimit();
1075     if (pp2catLimit) {
1076       pp2filter = pp2filter ? pp2filter + "," + pp2catLimit : pp2catLimit;
1077     }
1078
1079     var params = {};
1080     if (pp2limit) params.limit = pp2limit;
1081     if (maxrecs) params.maxrecs = maxrecs;
1082     if (torusquery) {
1083       if (!mkws.config.use_service_proxy)
1084         alert("can't narrow search by torusquery when not authenticated");
1085       params.torusquery = torusquery;
1086     }
1087
1088     log("triggerSearch(" + m_query + "): filters = " + m_filterSet.toJSON() + ", " +
1089         "pp2filter = " + pp2filter + ", params = " + $.toJSON(params));
1090
1091     m_paz.search(m_query, m_perpage, m_sortOrder, pp2filter, undefined, params);
1092   }
1093
1094   // fetch record details to be retrieved from the record queue
1095   that.fetchDetails = function(recId) {
1096     log("fetchDetails() requesting record '" + recId + "'");
1097     m_paz.record(recId);
1098   };
1099
1100
1101   // switching view between targets and records
1102   function switchView(view) {
1103     var targets = widgetNode('targets');
1104     var results = widgetNode('results') || widgetNode('records');
1105     var blanket = widgetNode('blanket');
1106     var motd    = widgetNode('motd');
1107
1108     switch(view) {
1109     case 'targets':
1110       if (targets) $(targets).show();
1111       if (results) $(results).hide();
1112       if (blanket) $(blanket).hide();
1113       if (motd) $(motd).hide();
1114       break;
1115     case 'records':
1116       if (targets) $(targets).hide();
1117       if (results) $(results).show();
1118       if (blanket) $(blanket).show();
1119       if (motd) $(motd).hide();
1120       break;
1121     default:
1122       alert("Unknown view '" + view + "'");
1123     }
1124   }
1125   that.switchView = switchView;
1126
1127
1128   // detailed record drawing
1129   that.showDetails = function(recId) {
1130     var oldRecordId = m_currentRecordId;
1131     m_currentRecordId = recId;
1132
1133     // remove current detailed view if any
1134     findnode('#' + recordDetailsId(oldRecordId)).remove();
1135
1136     // if the same clicked, just hide
1137     if (recId == oldRecordId) {
1138       m_currentRecordId = '';
1139       m_currentRecordData = null;
1140       return;
1141     }
1142     // request the record
1143     log("showDetails() requesting record '" + recId + "'");
1144     m_paz.record(recId);
1145   };
1146
1147
1148   // Finds the node of the specified class within the current team
1149   function findnode(selector, teamName) {
1150     teamName = teamName || m_teamName;
1151
1152     if (teamName === 'AUTO') {
1153       selector = (selector + '.mkws-team-' + teamName + ',' +
1154                   selector + ':not([class^="mkws-team"],[class*=" mkws-team"])');
1155     } else {
1156       selector = selector + '.mkws-team-' + teamName;
1157     }
1158
1159     var node = $(selector);
1160     //log('findnode(' + selector + ') found ' + node.length + ' nodes');
1161     return node;
1162   }
1163
1164
1165   function widgetNode(type) {
1166     var w = that.widget(type);
1167     return w ? w.node : undefined;
1168   }
1169
1170   function renderDetails(data, marker) {
1171     var template = loadTemplate("details");
1172     var details = template(data);
1173     return '<div class="mkws-details mkwsDetails mkwsTeam_' + m_teamName + '" ' +
1174       'id="' + recordDetailsId(data.recid[0]) + '">' + details + '</div>';
1175   }
1176   that.renderDetails = renderDetails;
1177
1178
1179   that.registerTemplate = function(name, text) {
1180     if(mkws._old2new.hasOwnProperty(name)) {
1181       mkws.log("Warning: registerTemplate old widget name: " + name + " => " + mkws._old2new[name]);
1182       name = mkws._old2new[name];
1183     }
1184     m_templateText[name] = text;
1185   };
1186
1187
1188   function loadTemplate(name, fallbackString) {
1189     if(mkws._old2new.hasOwnProperty(name)) {
1190        mkws.log("Warning loadTemplate: old widget name: " + name + " => " + mkws._old2new[name]);
1191        name = mkws._old2new[name];
1192     }
1193
1194     var template = m_template[name];
1195     if (template === undefined && Handlebars.compile) {
1196       var source;
1197       var node = $(".mkws-template-" + name + " .mkws-team-" + that.name());
1198       if (node && node.length < 1) {
1199         node = $(".mkws-template-" + name);
1200       }
1201       if (node) source = node.html();
1202       if (!source) source = m_templateText[name];
1203       if (source) {
1204         template = Handlebars.compile(source);
1205         log("compiled template '" + name + "'");
1206       }
1207     }
1208     //if (template === undefined) template = mkws_templatesbyteam[m_teamName][name];
1209     if (template === undefined && Handlebars.templates) {
1210       template = Handlebars.templates["mkws-template-" + name];
1211     }
1212     if (template === undefined && mkws.defaultTemplates) {
1213       template = mkws.defaultTemplates[name];
1214     }
1215     if (template) {
1216       m_template[name] = template;
1217       return template;
1218     }
1219     else {
1220       log("No MKWS template for " + name);
1221       return null;
1222     }  
1223   }
1224   that.loadTemplate = loadTemplate;
1225
1226
1227   that.addWidget = function(w) {
1228     if (m_widgets[w.type] === undefined) {
1229       m_widgets[w.type] = [ w ];
1230     } else {
1231       m_widgets[w.type].push(w);
1232     }
1233   }
1234
1235   that.widget = function(type) {
1236     var list = m_widgets[type];
1237
1238     if (!list)
1239       return undefined;
1240     if (list.length > 1) {
1241       alert("widget('" + type + "') finds " + list.length + " widgets: using first");
1242     }
1243     return list[0];
1244   }
1245
1246   that.visitWidgets = function(callback) {
1247     for (var type in m_widgets) {
1248       var list = m_widgets[type];
1249       for (var i = 0; i < list.length; i++) {
1250         var res = callback(type, list[i]);
1251         if (res !== undefined) {
1252           return res;
1253         }
1254       }
1255     }
1256     return undefined;
1257   }
1258
1259
1260   return that;
1261 };
1262 // Factory function for sets of filters.
1263 function filterSet(team) {
1264   var m_team = team;
1265   var m_list = [];
1266
1267   var that = {};
1268
1269   that.toJSON = function() {
1270     return mkws.$.toJSON(m_list);
1271   };
1272
1273   that.add = function(filter) {
1274     m_list.push(filter);
1275   };
1276
1277   that.visitTargets = function(callback) {
1278     for (var i in m_list) {
1279       var filter = m_list[i];
1280       if (filter.type === 'target') {
1281         callback(filter.id, filter.name);
1282       }
1283     }
1284   };
1285
1286   that.visitFields = function(callback) {
1287     for (var i in m_list) {
1288       var filter = m_list[i];
1289       if (filter.type === 'field') {
1290         callback(filter.field, filter.value);
1291       }
1292     }
1293   };
1294
1295   that.visitCategories = function(callback) {
1296     for (var i in m_list) {
1297       var filter = m_list[i];
1298       if (filter.type === 'category') {
1299         callback(filter.id);
1300       }
1301     }
1302   };
1303
1304   that.removeMatching = function(matchFn) {
1305     var newList = [];
1306     for (var i in m_list) {
1307       var filter = m_list[i];
1308       if (matchFn(filter)) {
1309         m_team.log("removeMatching: removing filter " + mkws.$.toJSON(filter));
1310       } else {
1311         m_team.log("removeMatching: keeping filter " + mkws.$.toJSON(filter));
1312         newList.push(filter);
1313       }
1314     }
1315     m_list = newList;
1316   };
1317
1318   that.targetFiltered = function(id) {
1319     for (var i = 0; i < m_list.length; i++) {
1320       if (m_list[i].type === 'target' ||
1321           m_list[i].id === 'pz:id=' + id) {
1322         return true;
1323       }
1324     }
1325     return false;
1326   };
1327
1328   that.pp2filter = function() {
1329     var res = "";
1330
1331     that.visitTargets(function(id, name) {
1332       if (res) res += ",";
1333       if (id.match(/^[a-z:]+[=~]/)) {
1334         m_team.log("filter '" + id + "' already begins with SETTING OP");
1335       } else {
1336         id = 'pz:id=' + id;
1337       }
1338       res += id;
1339     });
1340
1341     return res;
1342   };
1343
1344   that.pp2limit = function(initial) {
1345     var res = initial || "";
1346
1347     that.visitFields(function(field, value) {
1348       if (res) res += ",";
1349       res += field + "=" + value.replace(/[\\|,]/g, '\\$&');
1350     });
1351     return res;
1352   }
1353
1354   that.pp2catLimit = function() {
1355     var res = "";
1356
1357     that.visitCategories(function(id) {
1358       if (res) res += ",";
1359       res += "category~" + id.replace(/[\\|,]/g, '\\$&');
1360     });
1361     return res;
1362   }
1363
1364   return that;
1365 }
1366
1367
1368 // Factory functions for filters. These can be of several types.
1369 function targetFilter(id, name) {
1370   return {
1371     type: 'target',
1372     id: id,
1373     name: name
1374   };
1375 }
1376
1377 function fieldFilter(field, value) {
1378   return {
1379     type: 'field',
1380     field: field,
1381     value: value
1382   };
1383 }
1384
1385 function categoryFilter(id) {
1386   return {
1387     type: 'category',
1388     id: id
1389   };
1390 }
1391 // Factory function for widget objects.
1392 mkws.makeWidget = function($, team, type, node) {
1393   // Static register of attributes that do not contribute to config
1394   var ignoreAttrs = {
1395     id:1, 'class':1, style:1, name:1, action:1, type:1, size:1,
1396     value:1, width:1, valign:1
1397   };
1398
1399   var that = {
1400     team: team,
1401     type: type,
1402     node: $(node),
1403     config: mkws.objectInheritingFrom(team.config)
1404   };
1405
1406   function log(s) {
1407     team.log(s);
1408   }
1409   that.log = log;
1410
1411   that.toString = function() {
1412     return '[Widget ' + team.name() + ':' + type + ']';
1413   };
1414
1415   that.value = function() {
1416     return node.value;
1417   };
1418
1419   // Returns the HTML of a subwidget of the specified type. It gets
1420   // the same attributes at the parent widget that invokes this
1421   // function, except where overrides are passed in. If defaults are
1422   // also provided, then these are used when the parent widget
1423   // provides no values.
1424   that.subwidget = function(type, overrides, defaults) {
1425     var attrs = { _team: team.name() };
1426     
1427     // Copy locally-set properties from the parent widget
1428     for (var name in this.config) {
1429       if (this.config.hasOwnProperty(name)) {
1430         attrs[name] = this.config[name];
1431         log(this + " copied property " + name + "='" + attrs[name] + "' to " + type + " subwidget");
1432       }
1433     }
1434     
1435     for (var name in overrides) {
1436       log(this + " overrode property " + name + "='" + overrides[name] + "' (was '" + attrs[name] + "') for " + type + " subwidget");
1437       attrs[name] = overrides[name];
1438     }
1439
1440     if (defaults) {
1441       for (var name in defaults) {
1442         if (!attrs[name]) {
1443           attrs[name] = defaults[name];
1444           log(this + " fell back to default property " + name + "='" + attrs[name] + "' for " + type + " subwidget");
1445         }
1446       }
1447     }
1448
1449     var s = [];
1450     s.push('<div class="mkws', type, ' mkws-team-', attrs._team, '"');
1451     for (var name in attrs) {    
1452       if (name !== '_team')
1453         s.push(' ', name, '="', attrs[name], '"');
1454     }
1455     s.push('></div>');
1456     return s.join('');
1457   };
1458
1459   function expandValue(val) {
1460     if (val.match(/^!param!/)) {
1461       var param = val.replace(/^!param!/, '');
1462       val = mkws.getParameterByName(param);
1463       log("obtained val '" + val + "' from param '" + param + "'");
1464       if (!val) {
1465         alert("This page has a MasterKey widget that needs a val specified by the '" + param + "' parameter");
1466       }
1467     } else if (val.match(/^!path!/)) {
1468       var index = val.replace(/^!path!/, '');
1469       var path = window.location.pathname.split('/');
1470       val = path[path.length - index];
1471       log("obtained val '" + val + "' from path-component '" + index + "'");
1472       if (!val) {
1473         alert("This page has a MasterKey widget that needs a val specified by the path-component " + index);
1474       }
1475     } else if (val.match(/^!var!/)) {
1476       var name = val.replace(/^!var!/, '');
1477       val = window[name]; // It's ridiculous that this works
1478       log("obtained val '" + val + "' from variable '" + name + "'");
1479       if (!val) {
1480         alert("This page has a MasterKey widget that needs a val specified by the '" + name + "' variable");
1481       }
1482     }
1483     return val;
1484   };
1485
1486   // Utility function for use by all widgets that can invoke autosearch.
1487   that.autosearch = function() {
1488     var that = this;
1489     var query = this.config.autosearch;
1490     if (query) {
1491       var old = this.team.config.query;
1492       if (!old) {
1493         // Stash this for subsequent inspection
1494         this.team.config.query = query;
1495       } else if (old === query) {
1496         this.log("duplicate autosearch: '" + query + "': ignoring");
1497         return;
1498       } else {
1499         this.log("conflicting autosearch: '" + query + "' vs '" + old + "': ignoring");
1500         return;
1501       }
1502
1503       this.team.queue("ready").subscribe(function() {
1504         // Postpone testing for the configuration items: these are not
1505         // yet set for Record subclass widgets that fill them in in the
1506         // subclass, as widget.autosearch is called in the superclass,
1507         // before the subclass fiddles with the configuration.
1508         var sortOrder = that.config.sort;
1509         var maxrecs = that.config.maxrecs;
1510         var perpage = that.config.perpage;
1511         var limit = that.config.limit;
1512         var targets = that.config.targets;
1513         var targetfilter = that.config.targetfilter;
1514         var target = that.config.target;
1515         if (target) targetfilter = 'udb=="' + target + '"';
1516
1517         var s = "running auto search: '" + query + "'";
1518         if (sortOrder) s += " sorted by '" + sortOrder + "'";
1519         if (maxrecs) s += " restricted to " + maxrecs + " records";
1520         if (perpage) s += " with " + perpage + " per page";
1521         if (limit) s += " limited by '" + limit + "'";
1522         if (targets) s += " in targets '" + targets + "'";
1523         if (targetfilter) s += " constrained by targetfilter '" + targetfilter + "'";
1524         that.log(s);
1525
1526         that.team.newSearch(query, sortOrder, maxrecs, perpage, limit, targets, targetfilter);
1527       });
1528     }
1529   };
1530
1531   // Utility function for all widgets that want to hide in narrow windows
1532   that.hideWhenNarrow = function() {
1533     var that = this;
1534     this.team.queue("resize-narrow").subscribe(function(n) {
1535       that.node.hide();
1536     });
1537     this.team.queue("resize-wide").subscribe(function(n) {
1538       that.node.show();
1539     });
1540   };
1541
1542
1543   for (var i = 0; i < node.attributes.length; i++) {
1544     var a = node.attributes[i];
1545     var val = expandValue(a.value);
1546     if (a.name === 'data-mkws-config') {
1547       // Treat as a JSON fragment configuring just this widget
1548       log(node + ": parsing config fragment '" + val + "'");
1549       var data;
1550       try {
1551         data = $.parseJSON(val);
1552         for (var key in data) {
1553           log(node + ": adding config element " + key + "='" + data[key] + "'");
1554           that.config[key] = data[key];
1555         }
1556       } catch (err) {
1557         alert("Can't parse " + node + " data-mkws-config as JSON: " + val);
1558       }
1559     } else if (a.name.match (/^data-mkws-/)) {
1560       var name = a.name.replace(/^data-mkws-/, '')
1561       that.config[name] = val;
1562       log(that + ": set data-mkws attribute " + name + "='" + val + "'");
1563     } else if (!ignoreAttrs[a.name]) {
1564       that.config[a.name] = val;
1565       log(that + ": set regular attribute " + a.name + "='" + val + "'");
1566     }
1567   }
1568
1569   var fn = mkws.promotionFunction(type);
1570   if (fn) {
1571     fn.call(that);
1572     log("made " + type + " widget(node=" + node + ")");
1573   } else if (type.match(/-Container-(narrow|wide)$/)) {
1574     // Not really a widget: no need to log its lack of promotion
1575   } else {
1576     log("made UNPROMOTED widget(type=" + type + ", node=" + node + ")");
1577   }
1578
1579   return that;
1580 };
1581 (function($) { // jQuery wrapper
1582
1583 // Functions follow for promoting the regular widget object into
1584 // widgets of specific types. These could be moved into their own
1585 // source files.
1586
1587
1588 mkws.registerWidgetType('targets', function() {
1589   if (!this.config.show_switch) return;
1590   var that = this;
1591
1592   this.node.html('No information available yet.');
1593   this.node.css("display", "none");
1594
1595   this.team.queue("targets").subscribe(function(data) {
1596     // There is a bug in pz2.js wherein it makes each data object an array but
1597     // simply assigns properties to it.
1598     // TODO: remove this when PAZ-946 is addressed.
1599     var cleandata = [];
1600     for (var i = 0; i < data.length; i++) {
1601       var cur = {};
1602       cur.id = data[i].id;
1603       cur.hits = data[i].hits;
1604       cur.diagnostic = data[i].diagnostic;
1605       cur.message = data[i].message;
1606       cur.records = data[i].records;
1607       cur.state = data[i].state;
1608       cleandata.push(cur);
1609     }
1610
1611     var template = that.team.loadTemplate(that.config.template || "targets");
1612     that.node.html(template({data: cleandata}));
1613   });
1614 });
1615
1616
1617 mkws.registerWidgetType('stat', function() {
1618   var that = this;
1619   this.team.queue("stat").subscribe(function(data) {
1620     var template = that.team.loadTemplate(that.config.template || "stat");
1621     that.node.html(template(data));
1622   });
1623 });
1624
1625
1626 mkws.registerWidgetType('pager', function() {
1627   var that = this;
1628   var M = mkws.M;
1629
1630   this.team.queue("pager").subscribe(function(data) {
1631     var teamName = that.team.name();
1632     var output = {};
1633     output.first = data.start + 1;
1634     output.last = data.start + data.num;
1635     output.count = data.merged;
1636     output.found = data.total;
1637
1638     //client indexes pages from 1 but pz2 from 0
1639     var onsides = 5;
1640     var pages = Math.ceil(that.team.totalRecordCount() / that.team.perpage());
1641     var currentPage = that.team.currentPage();
1642
1643     var firstClkbl = (currentPage - onsides > 0)
1644       ? currentPage - onsides
1645       : 1;
1646     var lastClkbl = firstClkbl + 2*onsides < pages
1647       ? firstClkbl + 2*onsides
1648       : pages;
1649
1650     if (firstClkbl > 1) output.morePrev = true;
1651     if (lastClkbl < pages) output.moreNext = true;
1652
1653     if (currentPage > 1) output.prevClick = "mkws.pagerPrev(\'" + teamName + "\');";
1654
1655     output.pages = [];
1656     for(var i = firstClkbl; i <= lastClkbl; i++) {
1657       var o = {};
1658       o.number = i;
1659       if (i !== currentPage) {
1660         o.click = "mkws.showPage(\'" + teamName + "\', " + i + ");";
1661       }
1662       output.pages.push(o);
1663     }
1664
1665     if (pages - currentPage > 0) output.nextClick = "mkws.pagerNext(\'" + teamName + "\')";
1666
1667     var template = that.team.loadTemplate(that.config.template || "pager");
1668     that.node.html(template(output));
1669   });
1670 });
1671
1672 mkws.registerWidgetType('details', function() {
1673   var that = this;
1674   var recid = that.node.attr("data-mkws-recid");
1675   if (this.team.gotRecords()) { 
1676     that.team.fetchDetails(recid);
1677   } else {
1678     this.team.queue("firstrecords").subscribe(function() {
1679       that.team.fetchDetails(recid);
1680     });
1681   }
1682   this.team.queue("record").subscribe(function(data) {
1683     if ($.inArray(recid, data.recid) > -1) {
1684       var template = that.team.loadTemplate(that.config.template || "details");
1685       that.node.html(template(data));
1686     }
1687   });
1688 });
1689
1690 mkws.registerWidgetType('records', function() {
1691   var that = this;
1692   var team = this.team;
1693
1694   this.team.queue("records").subscribe(function(data) {
1695     for (var i = 0; i < data.hits.length; i++) {
1696       var hit = data.hits[i];
1697       hit.detailLinkId = team.recordElementId(hit.recid[0]);
1698       hit.detailClick = "mkws.showDetails('" + team.name() + "', '" + hit.recid[0] + "');return false;";
1699       hit.containerClass = "mkws-summary mkwsSummary mkws-team-" + team.name();
1700       hit.containerClass += " " + hit.detailLinkId;
1701       // ### At some point, we may be able to move the
1702       // m_currentRecordId and m_currentRecordData members
1703       // from the team object into this widget.
1704       if (hit.recid == team.currentRecordId()) {
1705         if (team.currentRecordData()) {
1706           hit.renderedDetails = team.renderDetails(team.currentRecordData());
1707         } 
1708       }
1709     }
1710     var template = team.loadTemplate(that.config.template || "records");
1711     var summaryPartial = team.loadTemplate(that.config['summary-template'] || "summary");
1712     var tdata = $.extend({}, {"hits": data.hits}, that.config.template_vars);
1713     that.node.html(template(tdata, {"partials":{"summary":summaryPartial}}));
1714   });
1715
1716   that.autosearch();
1717 });
1718
1719
1720 mkws.registerWidgetType('navi', function() {
1721   var that = this;
1722   var teamName = this.team.name();
1723
1724   this.team.queue("navi").subscribe(function() {
1725     var filters = that.team.filters();
1726     var output = {filters:[]};
1727
1728     filters.visitTargets(function(id, name) {
1729       var cur = {};
1730       cur.facet = 'source';
1731       cur.value = name;
1732       cur.click = "mkws.delimitTarget('" + teamName + "', '" + id + "'); return false;";
1733       output.filters.push(cur);
1734     });
1735
1736     filters.visitFields(function(field, value) {
1737       var cur = {};
1738       cur.facet = field;
1739       cur.value = value;
1740       cur.click = "mkws.delimitQuery('" + teamName + "', '" + field + "', '" + value + "'" + ");return false;";
1741       output.filters.push(cur);
1742     });
1743
1744     var template = that.team.loadTemplate(that.config.template || "navi");
1745     that.node.html(template(output));
1746   });
1747 });
1748
1749
1750 // It seems this and the Perpage widget doen't need to subscribe to
1751 // anything, since they produce events rather than consuming them.
1752 //
1753 mkws.registerWidgetType('sort', function() {
1754   var that = this;
1755
1756   this.node.change(function() {
1757     that.team.set_sortOrder(that.node.val());
1758     if (that.team.submitted()) {
1759       that.team.reShow();
1760     }
1761     return false;
1762   });
1763 });
1764
1765
1766 mkws.registerWidgetType('per-page', function() {
1767   var that = this;
1768
1769   this.node.change(function() {
1770     that.team.set_perpage(that.node.val());
1771     if (that.team.submitted()) {
1772       that.team.reShow();
1773     }
1774     return false;
1775   });
1776 });
1777
1778
1779 mkws.registerWidgetType('done', function() {
1780   var that = this;
1781   this.team.queue("complete").subscribe(function(n) {
1782     var template = that.team.loadTemplate(that.config.template || "done");
1783     that.node.html(template({count: n}));
1784   });
1785 });
1786
1787
1788 mkws.registerWidgetType('switch', function() {
1789   if (!this.config.show_switch) return;
1790   var tname = this.team.name();
1791   var output = {};
1792   output.recordClick = "mkws.switchView(\'" + tname + "\', \'records\')";
1793   output.targetClick = "mkws.switchView(\'" + tname + "\', \'targets\')";
1794   var template = this.team.loadTemplate(this.config.template || "switch");
1795   this.node.html(template(output));
1796   this.hideWhenNarrow();
1797 });
1798
1799
1800 mkws.registerWidgetType('search', function() {
1801   var output = {};
1802   output.team = this.team.name();
1803   var template = this.team.loadTemplate(this.config.template || "search");
1804   this.node.html(template(output));
1805 });
1806
1807
1808 mkws.registerWidgetType('search-form', function() {
1809   var team = this.team;
1810   this.node.submit(function() {
1811     var val = team.widget('query').value();
1812     team.newSearch(val);
1813     return false;
1814   });
1815 });
1816
1817
1818 mkws.registerWidgetType('results', function() {
1819   var template = this.team.loadTemplate(this.config.template || "results");
1820   this.node.html(template({team: this.team.name()}));
1821   this.autosearch();
1822 });
1823
1824
1825 mkws.registerWidgetType('ranking', function() {
1826   var output = {};
1827   output.perPage = [];
1828   output.sort = [];
1829   output.team = this.team.name();
1830   output.showSort = this.config.show_sort;
1831   output.showPerPage = this.config.show_perpage;
1832
1833   var order = this.team.sortOrder();
1834   this.log("making sort, sortOrder = '" + order + "'");
1835   for (var i = 0; i < this.config.sort_options.length; i++) {
1836     var cur = {};
1837     var opt = this.config.sort_options[i];
1838     cur.key = opt[0];
1839     cur.label = opt.length == 1 ? opt[0] : opt[1];
1840     if (order == cur.key || order == cur.label) cur.selected = true;
1841     output.sort.push(cur);
1842   }
1843
1844   var perpage = this.team.perpage();
1845   this.log("making perpage, perpage = " + perpage);
1846   for(var i = 0; i < this.config.perpage_options.length; i++) {
1847     var cur = {};
1848     cur.perPage = this.config.perpage_options[i];
1849     if (cur.perPage == perpage) cur.selected = true;
1850     output.perPage.push(cur);
1851   }
1852
1853   var template = this.team.loadTemplate(this.config.template || "ranking");
1854   this.node.html(template(output));
1855 });
1856
1857
1858 mkws.registerWidgetType('lang', function() {
1859   // dynamic URL or static page? /path/foo?query=test
1860   /* create locale language menu */
1861   if (!this.config.show_lang) return;
1862
1863   var lang_default = "en";
1864   var lang = this.config.lang || lang_default;
1865   var list = [];
1866
1867   /* display a list of configured languages, or all */
1868   var lang_options = this.config.lang_options;
1869   var toBeIncluded = {};
1870   for (var i = 0; i < lang_options.length; i++) {
1871     toBeIncluded[lang_options[i]] = true;
1872   }
1873
1874   for (var k in mkws.locale_lang) {
1875     if (toBeIncluded[k] || lang_options.length == 0) {
1876       cur = {};
1877       if (lang === k) cur.selected = true;
1878       cur.code = k;
1879       cur.url = lang_url(k);
1880       list.push(cur);
1881     }
1882   }
1883
1884   // add english link
1885   if (lang_options.length == 0 || toBeIncluded[lang_default]) {
1886       cur = {};
1887       if (lang === lang_default) cur.selected = true;
1888       cur.code = lang_default;
1889       cur.url = lang_url(lang_default);
1890       list.push(cur);
1891   }
1892
1893   this.log("language menu: " + list.join(", "));
1894
1895   var template = this.team.loadTemplate(this.config.template || "lang");
1896   this.node.html(template({languages: list}));
1897   this.hideWhenNarrow();
1898
1899   // set or re-set "lang" URL parameter
1900   function lang_url(lang) {
1901     var query = location.search;
1902     // no query parameters? done
1903     if (!query) {
1904       return "?lang=" + lang;
1905     }
1906
1907     // parameter does not exist
1908     if (!query.match(/[\?&]lang=/)) {
1909       return query + "&lang=" + lang;
1910     }
1911
1912     // replace existing parameter
1913     query = query.replace(/\?lang=([^&#;]*)/, "?lang=" + lang);
1914     query = query.replace(/\&lang=([^&#;]*)/, "&lang=" + lang);
1915     return query;
1916   }
1917 });
1918
1919
1920 mkws.registerWidgetType('motd', function() {
1921   var container = this.team.widget('motd-container');
1922   if (container) {
1923     // Move the MOTD from the provided element down into the container
1924     this.node.appendTo(container.node);
1925   }
1926 });
1927
1928
1929 // This widget has no functionality of its own, but its configuration
1930 // is copied up into its team, allowing it to affect other widgets in
1931 // the team.
1932 //
1933 mkws.registerWidgetType('config', function() {
1934   var c = this.config;
1935   for (var name in c) {
1936     if (c.hasOwnProperty(name)) {
1937       this.team.config[name] = c[name];
1938       this.log(this + " copied property " + name + "='" + c[name] + "' up to team");
1939     }
1940   }
1941 });
1942
1943
1944 mkws.registerWidgetType('progress', function() {
1945   var that = this;
1946   this.node.hide();
1947   this.team.queue("stat").subscribe(function(data) {
1948     var template = that.team.loadTemplate(that.config.template || "progress");
1949     that.node.html(template({
1950       done: data.clients - data.activeclients,
1951       waiting: data.activeclients
1952     }));
1953     that.node.show();
1954   });
1955 });
1956
1957
1958 // Some elements have mkws* classes that makes them appear as widgets
1959 // -- for example, because we want to style them using CSS -- but have
1960 // no actual functionality. We register these to prevent ignorable
1961 // warnings when they occur.
1962
1963 mkws.registerWidgetType('query', function() {});
1964 mkws.registerWidgetType('motd-container', function() {});
1965 mkws.registerWidgetType('button', function() {});
1966
1967
1968 })(mkws.$); // jQuery wrapper
1969 mkws.registerWidgetType('facets', function() {
1970   // Initially hide the facets; display when we get results
1971   var that = this;
1972   var team = this.team;
1973   team.queue("facets").subscribe(function(data) {
1974     that.node.addClass("active");
1975   });
1976
1977   var template = team.loadTemplate(this.config.template || "facets");
1978   this.node.html(template({
1979     team: team.name(),
1980     facets: this.config.facets
1981   }));
1982   this.autosearch();
1983 });
1984
1985
1986 mkws.registerWidgetType('facet', function() {
1987   var facetConfig = {
1988     xtargets: [ "Sources",  16, false ],
1989     subject:  [ "Subjects", 10, true ],
1990     author:   [ "Authors",  10, true ]
1991   }
1992   var that = this;
1993   var team = this.team;
1994   var name = that.config.facet;
1995   var ref = facetConfig[name] || [ "Unknown", 10, true ];
1996   var caption = this.config['facet_caption_' + name] || ref[0];
1997   var max     = parseInt(this.config['facet_max_' + name] || ref[1]);
1998   var pzIndex = ref[2] ? name : null;
1999
2000   that.toString = function() {
2001     return '[Widget ' + team.name() + ':' + that.type + '(' + name + ')]';
2002   };
2003
2004   team.queue("facets").subscribe(function(data) {
2005     data = data[name];
2006     var terms = [];
2007     var teamName = team.name();
2008     for (var i = 0; i < data.length && i < max; i++) {
2009       var linkdata = "";
2010       var action = "";
2011       if (!pzIndex) {
2012         // Special case: target selection
2013         linkdata += ('target_id='+data[i].id+' ');
2014         if (!team.targetFiltered(data[i].id)) {
2015           action = 'mkws.limitTarget(\'' + teamName + '\', this.getAttribute(\'target_id\'),this.firstChild.nodeValue)';
2016         }
2017       } else {
2018         action = 'mkws.limitQuery(\'' + teamName + '\', \'' + pzIndex + '\', this.firstChild.nodeValue)';
2019       }
2020       linkdata += 'onclick="' + action + ';return false;"';
2021       terms.push({
2022         term: data[i].name,
2023         field: data[i].id,
2024         count: data[i].freq,
2025         linkdata: linkdata
2026       }); 
2027     }
2028     // configured template > facet specific template > default facet template
2029     var template;
2030     if (that.config.template) {
2031       template = team.loadTemplate(that.config.template);
2032     } else {
2033       template = team.loadTemplate("facet-" + name);
2034       if (template) {
2035         that.log("Using facet-" + name + " template.")
2036       } else {
2037         that.log("No " + name + " specific template, using default.")
2038         template = team.loadTemplate("facet");
2039       }
2040     }
2041     that.node.html(template({
2042       name: name,
2043       caption: caption,
2044       query: that.config.query,
2045       terms: terms
2046     }));
2047   });
2048   this.autosearch();
2049 });
2050 mkws.registerWidgetType('auth-name', function() {
2051   var that = this;
2052
2053   this.team.queue("authenticated").subscribe(function(authName) {
2054     that.node.html(authName);
2055   });
2056 });
2057 mkws.registerWidgetType('categories', function() {
2058   var that = this;
2059
2060   if (!mkws.authenticated) {
2061     alert("can't use categories widget when not authenticated");
2062     return;
2063   }
2064
2065   this.team.queue("authenticated").subscribe(function(authName, realm) {
2066     var req = new pzHttpRequest(mkws.pazpar2_url() + "?command=categories", function(err) {
2067       alert("HTTP call for categories failed: " + err)
2068     });
2069
2070     req.get(null, function(data) {
2071       if (!$.isXMLDoc(data)) {
2072         alert("categories response document is not XML");
2073         return;
2074       }
2075       that.log("got categories: " + data);
2076
2077       var text = [];
2078       text.push("Select category: ");
2079       text.push("<select name='mkws-category mkwsCategory' " +
2080                 "onchange='mkws.limitCategory(\"" + that.team.name() + "\", this.value)'>");
2081       text.push("<option value=''>[All]</option>");
2082       $(data).find('category').each(function() {
2083         var name = $(this).find('categoryName').text();
2084         var id = $(this).find('categoryId').text();
2085         text.push("<option value='", id, "'>", name, "</option>");
2086       });
2087       text.push("</select>");
2088       that.node.html(text.join(''));
2089     });
2090   });
2091 });
2092 mkws.registerWidgetType('log', function() {
2093   var that = this;
2094
2095   this.team.queue("log").subscribe(function(teamName, timestamp, message) {
2096     that.node.append(teamName + ": " + timestamp + message + "<br/>");
2097   });
2098 });
2099 // A widget for a record-list of a single record
2100 mkws.registerWidgetType('record', function() {
2101   if (!this.config.maxrecs) this.config.maxrecs = 1;
2102   var that = this;
2103   var team = this.team;
2104   team.queue("records").subscribe(function(data) {
2105     var template = team.loadTemplate(that.config.template || "details");
2106     var targs = mkws.$.extend({}, data.hits[0], that.config.template_vars);
2107     that.node.html(template(targs));
2108   });
2109   that.autosearch();
2110 });
2111
2112 mkws.registerWidgetType('images', function() {
2113   mkws.promotionFunction('records').call(this);
2114   if (!this.config.template) this.config.template = 'images';
2115 });
2116
2117 mkws.registerWidgetType('google-image', function() {
2118   mkws.promotionFunction('images').call(this);
2119   if (!this.config.target) this.config.target = 'Google_Images';
2120 });
2121
2122 mkws.registerWidgetType('lolcat', function() {
2123   mkws.promotionFunction('google-image').call(this);
2124   if (!this.config.autosearch) this.config.autosearch = 'kitteh';
2125 });
2126
2127 mkws.registerWidgetType('cover-art', function() {
2128   mkws.promotionFunction('images').call(this);
2129   if (!this.config.target) this.config.target = 'AmazonBooks';
2130 });
2131 mkws.registerWidgetType('reference', function() {
2132   mkws.promotionFunction('record').call(this);
2133   if (!this.config.target) this.config.target = 'wikimedia_wikipedia_single_result';
2134   if (!this.config.template) this.config.template = 'reference';
2135   this.config.template_vars.paragraphs = this.config.paragraphs || 0;
2136   this.config.template_vars.sentences = this.config.sentences || 0;
2137 });
2138 mkws.registerWidgetType('builder', function() {
2139   var that = this;
2140   var team = this.team;
2141
2142   var button = mkws.$('<button/>', {
2143     type: 'button',
2144     text: this.config.text || "Build!"
2145   });
2146   this.node.append(button);
2147   button.click(function() {
2148     var   query = team.widget('query').value();
2149     var    sort = team.widget('sort').value();
2150     var perpage = team.widget('per-page').value();
2151
2152     var html = ('<div class="mkws-records mkwsRecords" ' +
2153                 'autosearch="' + query + '" ' +
2154                 'sort="' + sort + '" ' +
2155                 'perpage="' + perpage + '"></div>');
2156     var fn = that.callback || alert;
2157     fn(html);
2158   });
2159 });
2160
2161 mkws.registerWidgetType('console-builder', function() {
2162   mkws.promotionFunction('builder').call(this);
2163   this.callback = function(s) {
2164     console.log("generated widget: " + s);
2165   }
2166 });
2167 (function() {
2168   var template = Handlebars.template, templates = mkws.defaultTemplates = mkws.defaultTemplates || {};
2169 templates['details'] = template({"1":function(depth0,helpers,partials,data) {
2170   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2171   return "        ("
2172     + escapeExpression(((helper = (helper = helpers['md-title-remainder'] || (depth0 != null ? depth0['md-title-remainder'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title-remainder","hash":{},"data":data}) : helper)))
2173     + ")\n";
2174 },"3":function(depth0,helpers,partials,data) {
2175   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2176   return "        <i>"
2177     + escapeExpression(((helper = (helper = helpers['md-title-responsibility'] || (depth0 != null ? depth0['md-title-responsibility'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title-responsibility","hash":{},"data":data}) : helper)))
2178     + "</i>\n";
2179 },"5":function(depth0,helpers,partials,data) {
2180   var helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function";
2181   return "  <tr>\n    <th>"
2182     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Date", {"name":"mkws-translate","hash":{},"data":data})))
2183     + "</th>\n    <td>"
2184     + escapeExpression(((helper = (helper = helpers['md-date'] || (depth0 != null ? depth0['md-date'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-date","hash":{},"data":data}) : helper)))
2185     + "</td>\n  </tr>\n";
2186 },"7":function(depth0,helpers,partials,data) {
2187   var helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function";
2188   return "  <tr>\n    <th>"
2189     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Author", {"name":"mkws-translate","hash":{},"data":data})))
2190     + "</th>\n    <td>"
2191     + escapeExpression(((helper = (helper = helpers['md-author'] || (depth0 != null ? depth0['md-author'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-author","hash":{},"data":data}) : helper)))
2192     + "</td>\n  </tr>\n";
2193 },"9":function(depth0,helpers,partials,data) {
2194   var stack1, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "  <tr>\n    <th>"
2195     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Links", {"name":"mkws-translate","hash":{},"data":data})))
2196     + "</th>\n    <td>\n";
2197   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0['md-electronic-url'] : depth0), {"name":"each","hash":{},"fn":this.program(10, data),"inverse":this.noop,"data":data});
2198   if (stack1 != null) { buffer += stack1; }
2199   return buffer + "    </td>\n  </tr>\n";
2200 },"10":function(depth0,helpers,partials,data) {
2201   var helper, lambda=this.lambda, escapeExpression=this.escapeExpression, functionType="function", helperMissing=helpers.helperMissing;
2202   return "        <a href=\""
2203     + escapeExpression(lambda(depth0, depth0))
2204     + "\">Link"
2205     + escapeExpression(((helper = (helper = helpers['mkws-index1'] || (depth0 != null ? depth0['mkws-index1'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"mkws-index1","hash":{},"data":data}) : helper)))
2206     + "</a>\n";
2207 },"12":function(depth0,helpers,partials,data) {
2208   var stack1, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "  <tr>\n    <th>"
2209     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Subject", {"name":"mkws-translate","hash":{},"data":data})))
2210     + "</th>\n    <td>\n";
2211   stack1 = ((helpers['mkws-first'] || (depth0 && depth0['mkws-first']) || helperMissing).call(depth0, (depth0 != null ? depth0.location : depth0), {"name":"mkws-first","hash":{
2212     'having': ("md-subject")
2213   },"fn":this.program(13, data),"inverse":this.noop,"data":data}));
2214   if (stack1 != null) { buffer += stack1; }
2215   return buffer + "    </td>\n  </tr>\n";
2216 },"13":function(depth0,helpers,partials,data) {
2217   var stack1, buffer = "";
2218   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-subject'] : depth0), {"name":"if","hash":{},"fn":this.program(14, data),"inverse":this.noop,"data":data});
2219   if (stack1 != null) { buffer += stack1; }
2220   return buffer;
2221 },"14":function(depth0,helpers,partials,data) {
2222   var stack1, helperMissing=helpers.helperMissing, buffer = "";
2223   stack1 = ((helpers['mkws-commaList'] || (depth0 && depth0['mkws-commaList']) || helperMissing).call(depth0, (depth0 != null ? depth0['md-subject'] : depth0), {"name":"mkws-commaList","hash":{},"fn":this.program(15, data),"inverse":this.noop,"data":data}));
2224   if (stack1 != null) { buffer += stack1; }
2225   return buffer + "\n";
2226 },"15":function(depth0,helpers,partials,data) {
2227   var lambda=this.lambda, escapeExpression=this.escapeExpression;
2228   return "            "
2229     + escapeExpression(lambda(depth0, depth0));
2230 },"17":function(depth0,helpers,partials,data) {
2231   var helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2232   return "        "
2233     + escapeExpression(((helpers['mkws-attr'] || (depth0 && depth0['mkws-attr']) || helperMissing).call(depth0, "@name", {"name":"mkws-attr","hash":{},"data":data})));
2234 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2235   var stack1, helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function", buffer = "<table>\n  <tr>\n    <th>"
2236     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Title", {"name":"mkws-translate","hash":{},"data":data})))
2237     + "</th>\n    <td>\n      "
2238     + escapeExpression(((helper = (helper = helpers['md-title'] || (depth0 != null ? depth0['md-title'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title","hash":{},"data":data}) : helper)))
2239     + "\n";
2240   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-title-remainder'] : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2241   if (stack1 != null) { buffer += stack1; }
2242   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-title-responsibility'] : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data});
2243   if (stack1 != null) { buffer += stack1; }
2244   buffer += "    </td>\n  </tr>\n";
2245   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-date'] : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.noop,"data":data});
2246   if (stack1 != null) { buffer += stack1; }
2247   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-author'] : depth0), {"name":"if","hash":{},"fn":this.program(7, data),"inverse":this.noop,"data":data});
2248   if (stack1 != null) { buffer += stack1; }
2249   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-electronic-url'] : depth0), {"name":"if","hash":{},"fn":this.program(9, data),"inverse":this.noop,"data":data});
2250   if (stack1 != null) { buffer += stack1; }
2251   stack1 = ((helpers['mkws-if-any'] || (depth0 && depth0['mkws-if-any']) || helperMissing).call(depth0, (depth0 != null ? depth0.location : depth0), {"name":"mkws-if-any","hash":{
2252     'having': ("md-subject")
2253   },"fn":this.program(12, data),"inverse":this.noop,"data":data}));
2254   if (stack1 != null) { buffer += stack1; }
2255   buffer += "  <tr>\n    <th>"
2256     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Locations", {"name":"mkws-translate","hash":{},"data":data})))
2257     + "</th>\n    <td>\n";
2258   stack1 = ((helpers['mkws-commaList'] || (depth0 && depth0['mkws-commaList']) || helperMissing).call(depth0, (depth0 != null ? depth0.location : depth0), {"name":"mkws-commaList","hash":{},"fn":this.program(17, data),"inverse":this.noop,"data":data}));
2259   if (stack1 != null) { buffer += stack1; }
2260   return buffer + "\n    </td>\n  </tr>\n</table>\n";
2261 },"useData":true});
2262 templates['done'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2263   var stack1, helper, helperMissing=helpers.helperMissing, functionType="function", escapeExpression=this.escapeExpression, buffer = "";
2264   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Search complete: found", {"name":"mkws-translate","hash":{},"data":data}));
2265   if (stack1 != null) { buffer += stack1; }
2266   buffer += " "
2267     + escapeExpression(((helper = (helper = helpers.count || (depth0 != null ? depth0.count : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"count","hash":{},"data":data}) : helper)))
2268     + " ";
2269   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "records", {"name":"mkws-translate","hash":{},"data":data}));
2270   if (stack1 != null) { buffer += stack1; }
2271   return buffer + "\n\n";
2272 },"useData":true});
2273 templates['facet'] = template({"1":function(depth0,helpers,partials,data) {
2274   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "  <div class=\"mkws-term mkwsTerm\">\n    <a href=\"#\" ";
2275   stack1 = ((helper = (helper = helpers.linkdata || (depth0 != null ? depth0.linkdata : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"linkdata","hash":{},"data":data}) : helper));
2276   if (stack1 != null) { buffer += stack1; }
2277   return buffer + ">"
2278     + escapeExpression(((helper = (helper = helpers.term || (depth0 != null ? depth0.term : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"term","hash":{},"data":data}) : helper)))
2279     + "</a> <span>"
2280     + escapeExpression(((helper = (helper = helpers.count || (depth0 != null ? depth0.count : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"count","hash":{},"data":data}) : helper)))
2281     + "</span>\n  </div>\n";
2282 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2283   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "\n<div class=\"mkws-facet-title mkwsFacetTitle\">"
2284     + escapeExpression(((helper = (helper = helpers.caption || (depth0 != null ? depth0.caption : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"caption","hash":{},"data":data}) : helper)))
2285     + "</div>\n";
2286   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.terms : depth0), {"name":"each","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2287   if (stack1 != null) { buffer += stack1; }
2288   return buffer;
2289 },"useData":true});
2290 templates['facets'] = template({"1":function(depth0,helpers,partials,data,depths) {
2291   var lambda=this.lambda, escapeExpression=this.escapeExpression;
2292   return "  <div class=\"mkws-facet mkwsFacet mkws-team-"
2293     + escapeExpression(lambda((depths[1] != null ? depths[1].team : depths[1]), depth0))
2294     + "\" data-mkws-facet=\""
2295     + escapeExpression(lambda(depth0, depth0))
2296     + "\"></div>\n";
2297 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data,depths) {
2298   var stack1, buffer = "";
2299   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.facets : depth0), {"name":"each","hash":{},"fn":this.program(1, data, depths),"inverse":this.noop,"data":data});
2300   if (stack1 != null) { buffer += stack1; }
2301   return buffer;
2302 },"useData":true,"useDepths":true});
2303 templates['images'] = template({"1":function(depth0,helpers,partials,data,depths) {
2304   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "  <a href=\"#\" id=\""
2305     + escapeExpression(((helper = (helper = helpers.detailLinkId || (depth0 != null ? depth0.detailLinkId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"detailLinkId","hash":{},"data":data}) : helper)))
2306     + "\" onclick=\""
2307     + escapeExpression(((helper = (helper = helpers.detailClick || (depth0 != null ? depth0.detailClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"detailClick","hash":{},"data":data}) : helper)))
2308     + "\">\n";
2309   stack1 = ((helpers['mkws-first'] || (depth0 && depth0['mkws-first']) || helperMissing).call(depth0, (depth0 != null ? depth0['md-thumburl'] : depth0), {"name":"mkws-first","hash":{},"fn":this.program(2, data, depths),"inverse":this.noop,"data":data}));
2310   if (stack1 != null) { buffer += stack1; }
2311   return buffer + "    <br/>\n  </a>\n";
2312 },"2":function(depth0,helpers,partials,data,depths) {
2313   var lambda=this.lambda, escapeExpression=this.escapeExpression;
2314   return "      <img src=\""
2315     + escapeExpression(lambda(depth0, depth0))
2316     + "\" alt=\""
2317     + escapeExpression(lambda((depths[1] != null ? depths[1]['md-title'] : depths[1]), depth0))
2318     + "\"/>\n";
2319 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data,depths) {
2320   var stack1, buffer = "";
2321   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.hits : depth0), {"name":"each","hash":{},"fn":this.program(1, data, depths),"inverse":this.noop,"data":data});
2322   if (stack1 != null) { buffer += stack1; }
2323   return buffer;
2324 },"useData":true,"useDepths":true});
2325 templates['lang'] = template({"1":function(depth0,helpers,partials,data) {
2326   var stack1, buffer = "";
2327   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.selected : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.program(4, data),"data":data});
2328   if (stack1 != null) { buffer += stack1; }
2329   stack1 = helpers.unless.call(depth0, (data && data.last), {"name":"unless","hash":{},"fn":this.program(6, data),"inverse":this.noop,"data":data});
2330   if (stack1 != null) { buffer += stack1; }
2331   return buffer;
2332 },"2":function(depth0,helpers,partials,data) {
2333   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2334   return "<span>"
2335     + escapeExpression(((helper = (helper = helpers.code || (depth0 != null ? depth0.code : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"code","hash":{},"data":data}) : helper)))
2336     + "</span>";
2337 },"4":function(depth0,helpers,partials,data) {
2338   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<a href=\"";
2339   stack1 = ((helper = (helper = helpers.url || (depth0 != null ? depth0.url : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"url","hash":{},"data":data}) : helper));
2340   if (stack1 != null) { buffer += stack1; }
2341   return buffer + "\">"
2342     + escapeExpression(((helper = (helper = helpers.code || (depth0 != null ? depth0.code : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"code","hash":{},"data":data}) : helper)))
2343     + "</a>";
2344 },"6":function(depth0,helpers,partials,data) {
2345   return "    |\n";
2346   },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2347   var stack1, buffer = "";
2348   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.languages : depth0), {"name":"each","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2349   if (stack1 != null) { buffer += stack1; }
2350   return buffer + "\n";
2351 },"useData":true});
2352 templates['navi'] = template({"1":function(depth0,helpers,partials,data) {
2353   var stack1, helper, helperMissing=helpers.helperMissing, functionType="function", escapeExpression=this.escapeExpression, buffer = "  ";
2354   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, (depth0 != null ? depth0.facet : depth0), {"name":"mkws-translate","hash":{},"data":data}));
2355   if (stack1 != null) { buffer += stack1; }
2356   buffer += ": <a class=\"mkws-removable mkwsRemovable\" href=\"#\" onclick=\"";
2357   stack1 = ((helper = (helper = helpers.click || (depth0 != null ? depth0.click : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"click","hash":{},"data":data}) : helper));
2358   if (stack1 != null) { buffer += stack1; }
2359   buffer += "\">"
2360     + escapeExpression(((helper = (helper = helpers.value || (depth0 != null ? depth0.value : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"value","hash":{},"data":data}) : helper)))
2361     + "</a>\n  ";
2362   stack1 = helpers.unless.call(depth0, (data && data.last), {"name":"unless","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
2363   if (stack1 != null) { buffer += stack1; }
2364   return buffer + "\n";
2365 },"2":function(depth0,helpers,partials,data) {
2366   return "|";
2367   },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2368   var stack1, buffer = "";
2369   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.filters : depth0), {"name":"each","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2370   if (stack1 != null) { buffer += stack1; }
2371   return buffer + "\n";
2372 },"useData":true});
2373 templates['pager'] = template({"1":function(depth0,helpers,partials,data) {
2374   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "    <a href=\"#\" class=\"mkws-prev mkwsPrev\" onclick=\""
2375     + escapeExpression(((helper = (helper = helpers.prevClick || (depth0 != null ? depth0.prevClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"prevClick","hash":{},"data":data}) : helper)))
2376     + "\">&#60;&#60; ";
2377   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Prev", {"name":"mkws-translate","hash":{},"data":data}));
2378   if (stack1 != null) { buffer += stack1; }
2379   return buffer + "</a> |\n";
2380 },"3":function(depth0,helpers,partials,data) {
2381   var stack1, helperMissing=helpers.helperMissing, buffer = "    <span class=\"mkws-prev mkwsPrev\">&#60;&#60; ";
2382   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Prev", {"name":"mkws-translate","hash":{},"data":data}));
2383   if (stack1 != null) { buffer += stack1; }
2384   return buffer + "</span> |\n";
2385 },"5":function(depth0,helpers,partials,data) {
2386   return "...";
2387   },"7":function(depth0,helpers,partials,data) {
2388   var stack1, buffer = "";
2389   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.click : depth0), {"name":"if","hash":{},"fn":this.program(8, data),"inverse":this.program(10, data),"data":data});
2390   if (stack1 != null) { buffer += stack1; }
2391   return buffer;
2392 },"8":function(depth0,helpers,partials,data) {
2393   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2394   return "      <a href=\"#\" onclick=\""
2395     + escapeExpression(((helper = (helper = helpers.click || (depth0 != null ? depth0.click : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"click","hash":{},"data":data}) : helper)))
2396     + "\">"
2397     + escapeExpression(((helper = (helper = helpers.number || (depth0 != null ? depth0.number : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"number","hash":{},"data":data}) : helper)))
2398     + "</a>\n";
2399 },"10":function(depth0,helpers,partials,data) {
2400   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2401   return "      <span class=\"mkws-current-page mkwsCurrentPage\">"
2402     + escapeExpression(((helper = (helper = helpers.number || (depth0 != null ? depth0.number : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"number","hash":{},"data":data}) : helper)))
2403     + "</span>\n";
2404 },"12":function(depth0,helpers,partials,data) {
2405   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "    | <a href=\"#\" class=\"mkws-next mkwsNext\" onclick=\""
2406     + escapeExpression(((helper = (helper = helpers.nextClick || (depth0 != null ? depth0.nextClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nextClick","hash":{},"data":data}) : helper)))
2407     + "\">";
2408   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Next", {"name":"mkws-translate","hash":{},"data":data}));
2409   if (stack1 != null) { buffer += stack1; }
2410   return buffer + " &#62;&#62;</a>\n";
2411 },"14":function(depth0,helpers,partials,data) {
2412   var stack1, helperMissing=helpers.helperMissing, buffer = "    | <span class=\"mkws-next mkwsNext\">";
2413   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Next", {"name":"mkws-translate","hash":{},"data":data}));
2414   if (stack1 != null) { buffer += stack1; }
2415   return buffer + " &#62;&#62;</span>\n";
2416 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2417   var stack1, helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, functionType="function", buffer = "<div style=\"float: right\">\n  "
2418     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Displaying", {"name":"mkws-translate","hash":{},"data":data})))
2419     + ":\n  "
2420     + escapeExpression(((helper = (helper = helpers.first || (depth0 != null ? depth0.first : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"first","hash":{},"data":data}) : helper)))
2421     + " "
2422     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "to", {"name":"mkws-translate","hash":{},"data":data})))
2423     + " "
2424     + escapeExpression(((helper = (helper = helpers.last || (depth0 != null ? depth0.last : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"last","hash":{},"data":data}) : helper)))
2425     + "\n  "
2426     + escapeExpression(((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "of", {"name":"mkws-translate","hash":{},"data":data})))
2427     + " "
2428     + escapeExpression(((helper = (helper = helpers.count || (depth0 != null ? depth0.count : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"count","hash":{},"data":data}) : helper)))
2429     + " (";
2430   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "found", {"name":"mkws-translate","hash":{},"data":data}));
2431   if (stack1 != null) { buffer += stack1; }
2432   buffer += ": "
2433     + escapeExpression(((helper = (helper = helpers.found || (depth0 != null ? depth0.found : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"found","hash":{},"data":data}) : helper)))
2434     + ")\n</div>\n\n<div style=\"float: clear\">\n";
2435   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.prevClick : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
2436   if (stack1 != null) { buffer += stack1; }
2437   buffer += "\n  ";
2438   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.morePrev : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.noop,"data":data});
2439   if (stack1 != null) { buffer += stack1; }
2440   buffer += "\n\n";
2441   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.pages : depth0), {"name":"each","hash":{},"fn":this.program(7, data),"inverse":this.noop,"data":data});
2442   if (stack1 != null) { buffer += stack1; }
2443   buffer += "\n  ";
2444   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.moreNext : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.noop,"data":data});
2445   if (stack1 != null) { buffer += stack1; }
2446   buffer += "\n\n";
2447   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.nextClick : depth0), {"name":"if","hash":{},"fn":this.program(12, data),"inverse":this.program(14, data),"data":data});
2448   if (stack1 != null) { buffer += stack1; }
2449   return buffer + "</div>\n";
2450 },"useData":true});
2451 templates['progress'] = template({"1":function(depth0,helpers,partials,data) {
2452   return "&#x2588;";
2453   },"3":function(depth0,helpers,partials,data) {
2454   var stack1, helperMissing=helpers.helperMissing, buffer = "<span class=\"mkws-waiting mkws-waiting mkwsWaiting\">";
2455   stack1 = ((helpers['mkws-repeat'] || (depth0 && depth0['mkws-repeat']) || helperMissing).call(depth0, (depth0 != null ? depth0.waiting : depth0), {"name":"mkws-repeat","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data}));
2456   if (stack1 != null) { buffer += stack1; }
2457   return buffer + "</span>";
2458 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2459   var stack1, helperMissing=helpers.helperMissing, buffer = "<span class=\"mkws-done mkws-done mkwsDone\">";
2460   stack1 = ((helpers['mkws-repeat'] || (depth0 && depth0['mkws-repeat']) || helperMissing).call(depth0, (depth0 != null ? depth0.done : depth0), {"name":"mkws-repeat","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data}));
2461   if (stack1 != null) { buffer += stack1; }
2462   buffer += "</span>";
2463   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.waiting : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data});
2464   if (stack1 != null) { buffer += stack1; }
2465   return buffer;
2466 },"useData":true});
2467 templates['ranking'] = template({"1":function(depth0,helpers,partials,data) {
2468   var stack1, helper, helperMissing=helpers.helperMissing, functionType="function", escapeExpression=this.escapeExpression, buffer = "";
2469   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Sort by", {"name":"mkws-translate","hash":{},"data":data}));
2470   if (stack1 != null) { buffer += stack1; }
2471   buffer += "\n    <select class=\"mkws-sort mkwsSort mkws-team-"
2472     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2473     + "\">\n";
2474   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.sort : depth0), {"name":"each","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
2475   if (stack1 != null) { buffer += stack1; }
2476   return buffer + "    </select>";
2477 },"2":function(depth0,helpers,partials,data) {
2478   var stack1, buffer = "";
2479   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.selected : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.program(5, data),"data":data});
2480   if (stack1 != null) { buffer += stack1; }
2481   return buffer;
2482 },"3":function(depth0,helpers,partials,data) {
2483   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, buffer = "          <option value=\"";
2484   stack1 = ((helper = (helper = helpers.key || (depth0 != null ? depth0.key : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"key","hash":{},"data":data}) : helper));
2485   if (stack1 != null) { buffer += stack1; }
2486   buffer += "\" selected=\"selected\">";
2487   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, (depth0 != null ? depth0.label : depth0), {"name":"mkws-translate","hash":{},"data":data}));
2488   if (stack1 != null) { buffer += stack1; }
2489   return buffer + "</option>\n";
2490 },"5":function(depth0,helpers,partials,data) {
2491   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "          <option value=\""
2492     + escapeExpression(((helper = (helper = helpers.key || (depth0 != null ? depth0.key : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"key","hash":{},"data":data}) : helper)))
2493     + "\">";
2494   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, (depth0 != null ? depth0.label : depth0), {"name":"mkws-translate","hash":{},"data":data}));
2495   if (stack1 != null) { buffer += stack1; }
2496   return buffer + "</option>\n";
2497 },"7":function(depth0,helpers,partials,data) {
2498   var stack1, helper, helperMissing=helpers.helperMissing, functionType="function", escapeExpression=this.escapeExpression, buffer = "    ";
2499   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "and show", {"name":"mkws-translate","hash":{},"data":data}));
2500   if (stack1 != null) { buffer += stack1; }
2501   buffer += "\n    <select class=\"mkws-perpage mkwsPerpage mkws-team-"
2502     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2503     + "\">\n";
2504   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.perPage : depth0), {"name":"each","hash":{},"fn":this.program(8, data),"inverse":this.noop,"data":data});
2505   if (stack1 != null) { buffer += stack1; }
2506   buffer += "    </select>\n    ";
2507   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "per page", {"name":"mkws-translate","hash":{},"data":data}));
2508   if (stack1 != null) { buffer += stack1; }
2509   return buffer;
2510 },"8":function(depth0,helpers,partials,data) {
2511   var stack1, buffer = "";
2512   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.selected : depth0), {"name":"if","hash":{},"fn":this.program(9, data),"inverse":this.program(11, data),"data":data});
2513   if (stack1 != null) { buffer += stack1; }
2514   return buffer;
2515 },"9":function(depth0,helpers,partials,data) {
2516   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2517   return "          <option value=\""
2518     + escapeExpression(((helper = (helper = helpers.perPage || (depth0 != null ? depth0.perPage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"perPage","hash":{},"data":data}) : helper)))
2519     + "\" selected=\"selected\">"
2520     + escapeExpression(((helper = (helper = helpers.perPage || (depth0 != null ? depth0.perPage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"perPage","hash":{},"data":data}) : helper)))
2521     + "</option>\n";
2522 },"11":function(depth0,helpers,partials,data) {
2523   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2524   return "          <option value=\""
2525     + escapeExpression(((helper = (helper = helpers.perPage || (depth0 != null ? depth0.perPage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"perPage","hash":{},"data":data}) : helper)))
2526     + "\">"
2527     + escapeExpression(((helper = (helper = helpers.perPage || (depth0 != null ? depth0.perPage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"perPage","hash":{},"data":data}) : helper)))
2528     + "</option>\n";
2529 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2530   var stack1, buffer = "<form>";
2531   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.showSort : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2532   if (stack1 != null) { buffer += stack1; }
2533   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.showPerPage : depth0), {"name":"if","hash":{},"fn":this.program(7, data),"inverse":this.noop,"data":data});
2534   if (stack1 != null) { buffer += stack1; }
2535   return buffer + "</form>\n\n";
2536 },"useData":true});
2537 templates['records'] = template({"1":function(depth0,helpers,partials,data) {
2538   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "  <div class=\""
2539     + escapeExpression(((helper = (helper = helpers.containerClass || (depth0 != null ? depth0.containerClass : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"containerClass","hash":{},"data":data}) : helper)))
2540     + "\">\n";
2541   stack1 = this.invokePartial(partials.summary, '    ', 'summary', depth0, undefined, helpers, partials, data);
2542   if (stack1 != null) { buffer += stack1; }
2543   return buffer + "  </div>\n";
2544 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2545   var stack1, buffer = "";
2546   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.hits : depth0), {"name":"each","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2547   if (stack1 != null) { buffer += stack1; }
2548   return buffer;
2549 },"usePartial":true,"useData":true});
2550 templates['reference'] = template({"1":function(depth0,helpers,partials,data) {
2551   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2552   return "<b>"
2553     + escapeExpression(((helper = (helper = helpers['md-title-remainder'] || (depth0 != null ? depth0['md-title-remainder'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title-remainder","hash":{},"data":data}) : helper)))
2554     + "</b>\n";
2555 },"3":function(depth0,helpers,partials,data) {
2556   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2557   return "<i>"
2558     + escapeExpression(((helper = (helper = helpers['md-title-responsibility'] || (depth0 != null ? depth0['md-title-responsibility'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title-responsibility","hash":{},"data":data}) : helper)))
2559     + "</i>\n";
2560 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2561   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<img src=\""
2562     + escapeExpression(((helper = (helper = helpers['md-thumburl'] || (depth0 != null ? depth0['md-thumburl'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-thumburl","hash":{},"data":data}) : helper)))
2563     + "\" alt=\""
2564     + escapeExpression(((helper = (helper = helpers['md-title'] || (depth0 != null ? depth0['md-title'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title","hash":{},"data":data}) : helper)))
2565     + "\">\n<h1><a href=\""
2566     + escapeExpression(((helper = (helper = helpers['md-electronic-url'] || (depth0 != null ? depth0['md-electronic-url'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-electronic-url","hash":{},"data":data}) : helper)))
2567     + "\">"
2568     + escapeExpression(((helper = (helper = helpers['md-title'] || (depth0 != null ? depth0['md-title'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title","hash":{},"data":data}) : helper)))
2569     + "</a></h1>\n";
2570   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-title-remainder'] : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2571   if (stack1 != null) { buffer += stack1; }
2572   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-title-responsibility'] : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data});
2573   if (stack1 != null) { buffer += stack1; }
2574   stack1 = ((helpers['mkws-paragraphs'] || (depth0 && depth0['mkws-paragraphs']) || helperMissing).call(depth0, (depth0 != null ? depth0['md-description'] : depth0), (depth0 != null ? depth0.paragraphs : depth0), (depth0 != null ? depth0.sentences : depth0), {"name":"mkws-paragraphs","hash":{},"data":data}));
2575   if (stack1 != null) { buffer += stack1; }
2576   return buffer + "\n<p class=\"mkws-credit mkwsCredit\">Wikipedia</p>\n";
2577 },"useData":true});
2578 templates['results'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2579   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2580   return "<table width=\"100%\" border=\"0\" cellpadding=\"6\" cellspacing=\"0\">\n  <tr>\n    <td class=\"mkws-facets-container-wide mkws-team-"
2581     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2582     + "\" width=\"250\" valign=\"top\">\n      <div class=\"mkws-facets mkwsTermlists mkws-team-"
2583     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2584     + "\"></div>\n    </td>\n    <td class=\"mkws-motd-container mkwsMOTDContainer mkws-team-"
2585     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2586     + "\" valign=\"top\">\n      <div class=\"mkws-ranking mkwsRanking mkws-team-"
2587     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2588     + "\"></div>\n      <div class=\"mkws-pager mkwsPager mkws-team-"
2589     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2590     + "\"></div>\n      <div class=\"mkws-navi mkwsNavi mkws-team-"
2591     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2592     + "\"></div>\n      <div class=\"mkws-records mkwsRecords mkws-team-"
2593     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2594     + "\"></div>\n    </td>\n  </tr>\n  <tr>\n    <td colspan=\"2\">\n      <div class=\"mkws-facets-container-narrow mkws-team-"
2595     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2596     + "\"></div>\n    </td>\n  </tr>\n</table>\n\n";
2597 },"useData":true});
2598 templates['search'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2599   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<form name=\"mkws-search-form\" class=\"mkws-search-form mkws-team-"
2600     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2601     + "\" action=\"\">\n  <input class=\"mkws-query mkws-query mkwsQuery mkws-team-"
2602     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2603     + "\" type=\"text\"/>\n  <input class=\"mkws-button mkws-button mkwsButton mkws-team-"
2604     + escapeExpression(((helper = (helper = helpers.team || (depth0 != null ? depth0.team : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"team","hash":{},"data":data}) : helper)))
2605     + "\" type=\"submit\" value=\"";
2606   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Search", {"name":"mkws-translate","hash":{},"data":data}));
2607   if (stack1 != null) { buffer += stack1; }
2608   return buffer + "\"/>\n</form>\n\n";
2609 },"useData":true});
2610 templates['stat'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2611   var stack1, helper, helperMissing=helpers.helperMissing, functionType="function", escapeExpression=this.escapeExpression, buffer = " -- <span class=\"mkws-client-count mkwsClientCount\">";
2612   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Active clients", {"name":"mkws-translate","hash":{},"data":data}));
2613   if (stack1 != null) { buffer += stack1; }
2614   buffer += " : "
2615     + escapeExpression(((helper = (helper = helpers.activeclients || (depth0 != null ? depth0.activeclients : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"activeclients","hash":{},"data":data}) : helper)))
2616     + "/"
2617     + escapeExpression(((helper = (helper = helpers.clients || (depth0 != null ? depth0.clients : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"clients","hash":{},"data":data}) : helper)))
2618     + "</span> -- ";
2619   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Retrieved records", {"name":"mkws-translate","hash":{},"data":data}));
2620   if (stack1 != null) { buffer += stack1; }
2621   return buffer + " : "
2622     + escapeExpression(((helper = (helper = helpers.records || (depth0 != null ? depth0.records : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"records","hash":{},"data":data}) : helper)))
2623     + "/"
2624     + escapeExpression(((helper = (helper = helpers.hits || (depth0 != null ? depth0.hits : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"hits","hash":{},"data":data}) : helper)))
2625     + "\n";
2626 },"useData":true});
2627 templates['summary'] = template({"1":function(depth0,helpers,partials,data) {
2628   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, lambda=this.lambda;
2629   return "  <a class=\"mkws-field-thumb\" href=\"#\" onclick=\""
2630     + escapeExpression(((helper = (helper = helpers.detailClick || (depth0 != null ? depth0.detailClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"detailClick","hash":{},"data":data}) : helper)))
2631     + "\">\n    <img src=\""
2632     + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0['md-thumburl'] : depth0)) != null ? stack1['0'] : stack1), depth0))
2633     + "\"/>\n  </a>\n";
2634 },"3":function(depth0,helpers,partials,data) {
2635   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2636   return "    <span class=\"mkws-field-title-remainder\">"
2637     + escapeExpression(((helper = (helper = helpers['md-title-remainder'] || (depth0 != null ? depth0['md-title-remainder'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title-remainder","hash":{},"data":data}) : helper)))
2638     + "</span>\n";
2639 },"5":function(depth0,helpers,partials,data) {
2640   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2641   return "    <span class=\"mkws-field-author\">"
2642     + escapeExpression(((helper = (helper = helpers['md-author'] || (depth0 != null ? depth0['md-author'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-author","hash":{},"data":data}) : helper)))
2643     + "</span>\n";
2644 },"7":function(depth0,helpers,partials,data) {
2645   var stack1, buffer = "";
2646   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-title-responsibility'] : depth0), {"name":"if","hash":{},"fn":this.program(8, data),"inverse":this.noop,"data":data});
2647   if (stack1 != null) { buffer += stack1; }
2648   return buffer;
2649 },"8":function(depth0,helpers,partials,data) {
2650   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2651   return "      <span class=\"mkws-field-author\">"
2652     + escapeExpression(((helper = (helper = helpers['md-title-responsibility'] || (depth0 != null ? depth0['md-title-responsibility'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title-responsibility","hash":{},"data":data}) : helper)))
2653     + "</span>\n";
2654 },"10":function(depth0,helpers,partials,data) {
2655   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2656   return "    <div class=\"mkws-field-description\">"
2657     + escapeExpression(((helper = (helper = helpers['md-description'] || (depth0 != null ? depth0['md-description'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-description","hash":{},"data":data}) : helper)))
2658     + "</div>\n";
2659 },"12":function(depth0,helpers,partials,data) {
2660   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2661   return "    <span class=\"mkws-field-date\">"
2662     + escapeExpression(((helper = (helper = helpers['md-date'] || (depth0 != null ? depth0['md-date'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-date","hash":{},"data":data}) : helper)))
2663     + "</span>\n";
2664 },"14":function(depth0,helpers,partials,data) {
2665   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, buffer = "  ";
2666   stack1 = ((helper = (helper = helpers.renderedDetails || (depth0 != null ? depth0.renderedDetails : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"renderedDetails","hash":{},"data":data}) : helper));
2667   if (stack1 != null) { buffer += stack1; }
2668   return buffer + "\n";
2669 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2670   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "";
2671   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-thumburl'] : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2672   if (stack1 != null) { buffer += stack1; }
2673   buffer += "<div class=\"mkws-field-data\">\n  <span class=\"mkws-field-title\">\n  <a href=\"#\" id=\""
2674     + escapeExpression(((helper = (helper = helpers.detailLinkId || (depth0 != null ? depth0.detailLinkId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"detailLinkId","hash":{},"data":data}) : helper)))
2675     + "\" onclick=\""
2676     + escapeExpression(((helper = (helper = helpers.detailClick || (depth0 != null ? depth0.detailClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"detailClick","hash":{},"data":data}) : helper)))
2677     + "\">\n    "
2678     + escapeExpression(((helper = (helper = helpers['md-title'] || (depth0 != null ? depth0['md-title'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"md-title","hash":{},"data":data}) : helper)))
2679     + "\n  </a>\n  </span>\n";
2680   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-title-remainder'] : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data});
2681   if (stack1 != null) { buffer += stack1; }
2682   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-author'] : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.program(7, data),"data":data});
2683   if (stack1 != null) { buffer += stack1; }
2684   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-description'] : depth0), {"name":"if","hash":{},"fn":this.program(10, data),"inverse":this.noop,"data":data});
2685   if (stack1 != null) { buffer += stack1; }
2686   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['md-date'] : depth0), {"name":"if","hash":{},"fn":this.program(12, data),"inverse":this.noop,"data":data});
2687   if (stack1 != null) { buffer += stack1; }
2688   buffer += "</div>\n";
2689   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.renderedDetails : depth0), {"name":"if","hash":{},"fn":this.program(14, data),"inverse":this.noop,"data":data});
2690   if (stack1 != null) { buffer += stack1; }
2691   return buffer;
2692 },"useData":true});
2693 templates['switch'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2694   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, buffer = "<a href=\"#\" onclick=\"";
2695   stack1 = ((helper = (helper = helpers.recordClick || (depth0 != null ? depth0.recordClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"recordClick","hash":{},"data":data}) : helper));
2696   if (stack1 != null) { buffer += stack1; }
2697   buffer += "\">";
2698   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Records", {"name":"mkws-translate","hash":{},"data":data}));
2699   if (stack1 != null) { buffer += stack1; }
2700   buffer += "</a>\n<span>|</span>\n<a href=\"#\" onclick=\"";
2701   stack1 = ((helper = (helper = helpers.targetClick || (depth0 != null ? depth0.targetClick : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"targetClick","hash":{},"data":data}) : helper));
2702   if (stack1 != null) { buffer += stack1; }
2703   buffer += "\">";
2704   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Targets", {"name":"mkws-translate","hash":{},"data":data}));
2705   if (stack1 != null) { buffer += stack1; }
2706   return buffer + "</a>\n";
2707 },"useData":true});
2708 templates['targets'] = template({"1":function(depth0,helpers,partials,data) {
2709   var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "    <tr>\n      <td>";
2710   stack1 = ((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper));
2711   if (stack1 != null) { buffer += stack1; }
2712   buffer += "</td>\n      <td>"
2713     + escapeExpression(((helper = (helper = helpers.hits || (depth0 != null ? depth0.hits : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"hits","hash":{},"data":data}) : helper)))
2714     + "</td>\n      <td>\n";
2715   stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.message : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
2716   if (stack1 != null) { buffer += stack1; }
2717   return buffer + "      </td>\n      <td>"
2718     + escapeExpression(((helper = (helper = helpers.records || (depth0 != null ? depth0.records : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"records","hash":{},"data":data}) : helper)))
2719     + "</td>\n      <td>"
2720     + escapeExpression(((helper = (helper = helpers.state || (depth0 != null ? depth0.state : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"state","hash":{},"data":data}) : helper)))
2721     + "</td>\n    </tr>\n";
2722 },"2":function(depth0,helpers,partials,data) {
2723   var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
2724   return "         "
2725     + escapeExpression(((helper = (helper = helpers.diagnostic || (depth0 != null ? depth0.diagnostic : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"diagnostic","hash":{},"data":data}) : helper)))
2726     + " ("
2727     + escapeExpression(((helper = (helper = helpers.message || (depth0 != null ? depth0.message : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"message","hash":{},"data":data}) : helper)))
2728     + ")\n";
2729 },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
2730   var stack1, helperMissing=helpers.helperMissing, buffer = "<table>\n  <thead>\n    <tr>\n      <td>";
2731   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Target ID", {"name":"mkws-translate","hash":{},"data":data}));
2732   if (stack1 != null) { buffer += stack1; }
2733   buffer += "</td>\n      <td>";
2734   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Hits", {"name":"mkws-translate","hash":{},"data":data}));
2735   if (stack1 != null) { buffer += stack1; }
2736   buffer += "</td>\n      <td>";
2737   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Diags", {"name":"mkws-translate","hash":{},"data":data}));
2738   if (stack1 != null) { buffer += stack1; }
2739   buffer += "</td>\n      <td>";
2740   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "Records", {"name":"mkws-translate","hash":{},"data":data}));
2741   if (stack1 != null) { buffer += stack1; }
2742   buffer += "</td>\n      <td>";
2743   stack1 = ((helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']) || helperMissing).call(depth0, "State", {"name":"mkws-translate","hash":{},"data":data}));
2744   if (stack1 != null) { buffer += stack1; }
2745   buffer += "</td>\n      </td>\n    </tr>\n  </thead>\n  <tbody>\n";
2746   stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.data : depth0), {"name":"each","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
2747   if (stack1 != null) { buffer += stack1; }
2748   return buffer + "  </tbody>\n</table>\n";
2749 },"useData":true});
2750 })();/* generic function to open results in a popup window
2751  *
2752  */
2753
2754 //"use strict";
2755 // $(document).ready(function () {
2756 mkws.registerWidgetType('popup', function() {
2757     var $ = mkws.$;
2758     var debug = this.log;
2759     debug("init popup window");
2760
2761     var popup_window = $(this.node);
2762     // var popup_window = $(".mkws-popup mkwsPopup"); // $(document).ready()
2763     if (!popup_window) {
2764         debug("no popup found, skip...");
2765         return;
2766     } else {
2767         debug("number of popup windows found: " + popup_window.length);
2768     }
2769
2770     if (!$.ui) {
2771         alert("Error: jquery-ui.js is missing, did you include it after jQuery core in the HTML file?");
2772         return;
2773     }
2774
2775     // more than one widget on a page are possible
2776     popup_window.each(function(i) {
2777         var that = $(this);
2778
2779         // all atributes are strings, convert them to integers here
2780         var width = parseInt(that.attr("popup_width") || "800");
2781         var height = parseInt(that.attr("popup_height") || "600");
2782         var autoOpen = parseInt(that.attr("popup_autoOpen") || "0");
2783         var modal = parseInt(that.attr("popup_modal") || "0");
2784
2785         debug("Popup parameters: width: " + width + ", height: " + height + ", autoOpen: " + autoOpen);
2786         that.dialog({
2787             closeOnEscape: true,
2788             autoOpen: autoOpen,
2789             height: height,
2790             width: width,
2791             modal: modal ? true : false,
2792             resizable: true,
2793             buttons: {
2794                 Cancel: function() {
2795                     that.dialog("close");
2796                 }
2797             },
2798             close: function() {}
2799         });
2800
2801         // open at search query submit: "input.mkws-button mkwsButton"
2802         var id_botton = that.attr("popup_button");
2803         if (id_botton) {
2804             $(id_botton).button().click(function() {
2805                 that.dialog("open");
2806             });
2807         }
2808     });
2809 });