Eliminate temporary state mkws.usesessions
[mkws-moved-to-github.git] / tools / htdocs / mkws.js
1 /*! MKWS, the MasterKey Widget Set. Copyright (C) 2013-2014, Index Data */
2
3 "use strict"; // HTML5: disable for debug_level >= 2
4
5 // Set up global mkws object. Contains a hash of session objects,
6 // indexed by windowid.
7 var mkws = {
8     authenticated: false,
9     sessions: {}
10 };
11
12 mkws.locale_lang = {
13     "de": {
14         "Authors": "Autoren",
15         "Subjects": "Schlagwörter",
16         "Sources": "Daten und Quellen",
17         "Termlists": "Termlisten",
18         "Next": "Weiter",
19         "Prev": "Zurück",
20         "Search": "Suche",
21         "Sort by": "Sortieren nach",
22         "and show": "und zeige",
23         "per page": "pro Seite",
24         "Displaying": "Zeige",
25         "to": "von",
26         "of": "aus",
27         "found": "gefunden",
28         "Title": "Titel",
29         "Author": "Autor",
30         "Date": "Datum",
31         "Subject": "Schlagwort",
32         "Location": "Ort",
33         "Records": "Datensätze",
34         "Targets": "Datenbanken",
35
36         "dummy": "dummy"
37     },
38
39     "da": {
40         "Authors": "Forfattere",
41         "Subjects": "Emner",
42         "Sources": "Kilder",
43         "Termlists": "Termlists",
44         "Next": "Næste",
45         "Prev": "Forrige",
46         "Search": "Søg",
47         "Sort by": "Sorter efter",
48         "and show": "og vis",
49         "per page": "per side",
50         "Displaying": "Viser",
51         "to": "til",
52         "of": "ud af",
53         "found": "fandt",
54         "Title": "Title",
55         "Author": "Forfatter",
56         "Date": "Dato",
57         "Subject": "Emneord",
58         "Location": "Lokation",
59         "Records": "Poster",
60         "Targets": "Baser",
61
62         "dummy": "dummy"
63     }
64 };
65
66 // Define empty mkws_config for simple applications that don't define it.
67 if (mkws_config == null || typeof mkws_config != 'object') {
68     var mkws_config = {};
69 }
70
71 // wrapper for jQuery lib
72 function _make_mkws_team($, teamName) {
73     if (console && console.log)
74         console.log("run _make_mkws_team(" + (teamName ? teamName : "") + ")");
75
76     // call this function only once
77     if (mkws.init) {
78         alert("_make_mkws_team() called twice: how did that happen?!");
79         return;
80     }
81
82 var m_sort = 'relevance';
83 var m_filters = [];
84
85 // keep time state for debugging
86 var m_debug_time = {
87     "start": $.now(),
88     "last": $.now()
89 };
90
91 mkws.debug_function = function (string) {
92     if (!mkws.debug_level)
93         return;
94
95     if (typeof console === "undefined" || typeof console.log === "undefined") { /* ARGH!!! old IE */
96         return;
97     }
98
99     var now = $.now();
100     var timestamp = ((now - m_debug_time.start)/1000).toFixed(3) + " (+" + ((now - m_debug_time.last)/1000).toFixed(3) + ") "
101     m_debug_time.last = now;
102
103     // you need to disable use strict at the top of the file!!!
104     if (mkws.debug_level >= 3) {
105         console.log(timestamp + arguments.callee.caller);
106     } else if (mkws.debug_level >= 2) {
107         console.log(timestamp + ">>> called from function " + arguments.callee.caller.name + ' <<<');
108     }
109     console.log(timestamp + string);
110 }
111 var debug = mkws.debug_function; // local alias
112 debug("start running MKWS");
113
114
115 Handlebars.registerHelper('json', function(obj) {
116     return $.toJSON(obj);
117 });
118
119
120 Handlebars.registerHelper('translate', function(s) {
121     debug("translating '" + s + "'");
122     return M(s);
123 });
124
125
126 // We need {{attr '@name'}} because Handlebars can't parse {{@name}}
127 Handlebars.registerHelper('attr', function(attrName) {
128     return this[attrName];
129 });
130
131
132 /*
133  * Use as follows: {{#if-any NAME1 having="NAME2"}}
134  * Applicable when NAME1 is the name of an array
135  * The guarded code runs only if at least one element of the NAME1
136  * array has a subelement called NAME2.
137  */
138 Handlebars.registerHelper('if-any', function(items, options) {
139     var having = options.hash.having;
140     for (var i in items) {
141         var item = items[i]
142         if (!having || item[having]) {
143             return options.fn(this);
144         }
145     }
146     return "";
147 });
148
149
150 Handlebars.registerHelper('first', function(items, options) {
151     var having = options.hash.having;
152     for (var i in items) {
153         var item = items[i]
154         if (!having || item[having]) {
155             return options.fn(item);
156         }
157     }
158     return "";
159 });
160
161
162 Handlebars.registerHelper('commaList', function(items, options) {
163     var out = "";
164
165     for (var i in items) {
166         if (i > 0) out += ", ";
167         out += options.fn(items[i])
168     }
169
170     return out;
171 });
172
173
174 {
175
176     /* default mkws config */
177     var config_default = {
178         use_service_proxy: true,
179         pazpar2_url: "http://mkws.indexdata.com/service-proxy/",
180         service_proxy_auth: "http://mkws.indexdata.com/service-proxy-auth",
181         lang: "",
182         sort_options: [["relevance"], ["title:1", "title"], ["date:0", "newest"], ["date:1", "oldest"]],
183         perpage_options: [10, 20, 30, 50],
184         sort_default: "relevance",
185         perpage_default: 20,
186         query_width: 50,
187         show_lang: true,        /* show/hide language menu */
188         show_sort: true,        /* show/hide sort menu */
189         show_perpage: true,     /* show/hide perpage menu */
190         lang_options: [],       /* display languages links for given languages, [] for all */
191         facets: ["sources", "subjects", "authors"], /* display facets, in this order, [] for none */
192         responsive_design_width: undefined, /* a page with less pixel width considered as narrow */
193         debug_level: 1,     /* debug level for development: 0..2 */
194
195         dummy: "dummy"
196     };
197
198     /* set global debug_level flag early */
199     if (typeof mkws_config.debug_level !== 'undefined') {
200         mkws.debug_level = mkws_config.debug_level;
201     } else if (typeof config_default.debug_level !== 'undefined') {
202         mkws.debug_level = config_default.debug_level;
203     }
204
205     // make sure the mkws_config is a valid hash
206     if (!$.isPlainObject(mkws_config)) {
207         debug("ERROR: mkws_config is not an JS object, ignore it....");
208         mkws_config = {};
209     }
210
211     /* override standard config values by function parameters */
212     for (var k in config_default) {
213         if (typeof mkws_config[k] === 'undefined')
214            mkws_config[k] = config_default[k];
215         debug("Set config: " + k + ' => ' + mkws_config[k]);
216     }
217 }
218
219
220 m_sort = mkws_config.sort_default;
221 debug("copied mkws_config.sort_default '" + mkws_config.sort_default + "' to m_sort");
222
223 if (mkws_config.query_width < 5 || mkws_config.query_width > 150) {
224     debug("Reset query width: " + mkws_config.query_width);
225     mkws_config.query_width = 50;
226 }
227
228 for (var key in mkws_config) {
229     if (mkws_config.hasOwnProperty(key)) {
230         if (key.match(/^language_/)) {
231             var lang = key.replace(/^language_/, "");
232             // Copy custom languages into list
233             mkws.locale_lang[lang] = mkws_config[key];
234             debug("Added locally configured language '" + lang + "'");
235         }
236     }
237 }
238
239 // protocol independend link for pazpar2: "//mkws/sp" -> "https://mkws/sp"
240 if (mkws_config.pazpar2_url.match(/^\/\//)) {
241     mkws_config.pazpar2_url = document.location.protocol + mkws_config.pazpar2_url;
242     debug("adjust protocol independend links: " + mkws_config.pazpar2_url);
243 }
244
245 debug("Create main pz2 object");
246 // create a parameters array and pass it to the pz2's constructor
247 // then register the form submit event with the pz2.search function
248 // autoInit is set to true on default
249 var my_paz = new pz2( { "onshow": my_onshow,
250                     "showtime": 500,            //each timer (show, stat, term, bytarget) can be specified this way
251                     "pazpar2path": mkws_config.pazpar2_url,
252                     "oninit": my_oninit,
253                     "onstat": my_onstat,
254                     "onterm": my_onterm,
255                     "termlist": "xtargets,subject,author",
256                     "onbytarget": my_onbytarget,
257                     "usesessions" : mkws_config.use_service_proxy ? false : true,
258                     "showResponseType": '', // or "json" (for debugging?)
259                     "onrecord": my_onrecord } );
260
261 mkws.my_paz = my_paz; // export
262
263 // some state vars
264 var curPage = 1;
265 var recPerPage = 20;
266 var totalRec = 0;
267 var curDetRecId = '';
268 var curDetRecData = null;
269 var submitted = false;
270 var SourceMax = 16;
271 var SubjectMax = 10;
272 var AuthorMax = 10;
273
274 if (!isNaN(parseInt(mkws_config.perpage_default))) {
275     recPerPage = parseInt(mkws_config.perpage_default);
276 }
277
278 //
279 // pz2.js event handlers:
280 //
281 function my_oninit() {
282     my_paz.stat();
283     my_paz.bytarget();
284 }
285
286 function my_onshow(data) {
287     totalRec = data.merged;
288     // move it out
289     var pager = document.getElementById("mkwsPager");
290     if (pager) {
291         pager.innerHTML = "";
292         pager.innerHTML +='<div style="float: right">' + M('Displaying') + ': '
293             + (data.start + 1) + ' ' + M('to') + ' ' + (data.start + data.num) +
294             ' ' + M('of') + ' ' + data.merged + ' (' + M('found') + ': '
295             + data.total + ')</div>';
296         drawPager(pager);
297     }
298
299     // navi
300     var results = document.getElementById("mkwsRecords");
301
302     var html = [];
303     for (var i = 0; i < data.hits.length; i++) {
304         var hit = data.hits[i];
305         html.push('<div class="record" id="mkwsRecdiv_' + hit.recid + '" >',
306                   renderSummary(hit),
307                   '</div>');
308         if (hit.recid == curDetRecId) {
309             html.push(renderDetails(curDetRecData));
310         }
311     }
312     replaceHtml(results, html.join(''));
313 }
314
315
316 function renderSummary(hit)
317 {
318     if (mkws.templateSummary === undefined) {
319         loadTemplate("Summary");
320     }
321
322     hit._id = "mkwsRec_" + hit.recid;
323     hit._onclick = "mkws.showDetails(this.id);return false;"
324     return mkws.templateSummary(hit);
325 }
326
327
328 function my_onstat(data) {
329     var stat = document.getElementById("mkwsStat");
330     if (stat == null)
331         return;
332
333     stat.innerHTML = '<span class="head">' + M('Status info') + '</span>' +
334         ' -- ' +
335         '<span class="clients">' + M('Active clients') + ': ' + data.activeclients + '/' + data.clients + '</span>' +
336         ' -- ' +
337         '<span class="records">' + M('Retrieved records') + ': ' + data.records + '/' + data.hits + '</span>';
338 }
339
340 function my_onterm(data) {
341     // no facets
342     if (!mkws_config.facets || mkws_config.facets.length == 0) {
343         $("#mkwsTermlists").hide();
344         return;
345     }
346
347     // display if we first got results
348     $("#mkwsTermlists").show();
349
350     var acc = [];
351     acc.push('<div class="title">' + M('Termlists') + '</div>');
352     var facets = mkws_config.facets;
353
354     for(var i = 0; i < facets.length; i++) {
355         if (facets[i] == "sources") {
356             add_single_facet(acc, "Sources",  data.xtargets, SourceMax, null);
357         } else if (facets[i] == "subjects") {
358             add_single_facet(acc, "Subjects", data.subject,  SubjectMax, "subject");
359         } else if (facets[i] == "authors") {
360             add_single_facet(acc, "Authors",  data.author,   AuthorMax, "author");
361         } else {
362             alert("bad facet configuration: '" + facets[i] + "'");
363         }
364     }
365
366     var termlist = document.getElementById("mkwsTermlists");
367     if (termlist)
368         replaceHtml(termlist, acc.join(''));
369 }
370
371 function add_single_facet(acc, caption, data, max, pzIndex) {
372     acc.push('<div class="facet" id="mkwsFacet' + caption + '">');
373     acc.push('<div class="termtitle">' + M(caption) + '</div>');
374     for (var i = 0; i < data.length && i < max; i++ ) {
375         acc.push('<div class="term">');
376         acc.push('<a href="#" ');
377         var action;
378         if (!pzIndex) {
379             // Special case: target selection
380             acc.push('target_id='+data[i].id+' ');
381             action = 'mkws.limitTarget(this.getAttribute(\'target_id\'),this.firstChild.nodeValue)';
382         } else {
383             action = 'mkws.limitQuery(\'' + pzIndex + '\', this.firstChild.nodeValue)';
384         }
385         acc.push('onclick="' + action + ';return false;">' + data[i].name + '</a>'
386                  + ' <span>' + data[i].freq + '</span>');
387         acc.push('</div>');
388     }
389     acc.push('</div>');
390 }
391
392 function my_onrecord(data) {
393     // FIXME: record is async!!
394     clearTimeout(my_paz.recordTimer);
395     // in case on_show was faster to redraw element
396     var detRecordDiv = document.getElementById('mkwsDet_'+data.recid);
397     if (detRecordDiv) return;
398     curDetRecData = data;
399     var recordDiv = document.getElementById('mkwsRecdiv_'+curDetRecData.recid);
400     var html = renderDetails(curDetRecData);
401     recordDiv.innerHTML += html;
402 }
403
404 function my_onbytarget(data) {
405     var targetDiv = document.getElementById("mkwsBytarget");
406     if (!targetDiv) {
407         // No mkwsTargets div.
408         return;
409     }
410
411     var table ='<table><thead><tr>' +
412         '<td>' + M('Target ID') + '</td>' +
413         '<td>' + M('Hits') + '</td>' +
414         '<td>' + M('Diags') + '</td>' +
415         '<td>' + M('Records') + '</td>' +
416         '<td>' + M('State') + '</td>' +
417         '</tr></thead><tbody>';
418
419     for (var i = 0; i < data.length; i++ ) {
420         table += "<tr><td>" + data[i].id +
421             "</td><td>" + data[i].hits +
422             "</td><td>" + data[i].diagnostic +
423             "</td><td>" + data[i].records +
424             "</td><td>" + data[i].state + "</td></tr>";
425     }
426
427     table += '</tbody></table>';
428     targetDiv.innerHTML = table;
429 }
430
431 ////////////////////////////////////////////////////////////////////////////////
432 ////////////////////////////////////////////////////////////////////////////////
433
434 // wait until the DOM is ready
435 function domReady ()
436 {
437     document.mkwsSearchForm.onsubmit = onFormSubmitEventHandler;
438     document.mkwsSearchForm.mkwsQuery.value = '';
439     if (document.mkwsSelect) {
440         if (document.mkwsSelect.mkwsSort)
441             document.mkwsSelect.mkwsSort.onchange = onSelectDdChange;
442         if (document.mkwsSelect.mkwsPerpage)
443             document.mkwsSelect.mkwsPerpage.onchange = onSelectDdChange;
444     }
445 }
446
447 // when search button pressed
448 function onFormSubmitEventHandler()
449 {
450     newSearch(document.mkwsSearchForm.mkwsQuery.value);
451     return false;
452 }
453
454 function newSearch(query, sort, targets, windowid)
455 {
456     debug("newSearch: " + query);
457
458     if (mkws_config.use_service_proxy && !mkws.authenticated) {
459         alert("searching before authentication");
460         return;
461     }
462
463     m_filters = []
464     redraw_navi(); // ### should use windowid
465     resetPage(); // ### the globals it resents should be indexed by windowid
466     loadSelect(); // ### should use windowid
467     triggerSearch(query, sort, targets, windowid);
468     mkws.switchView('records'); // In case it's configured to start off as hidden
469     submitted = true;
470 }
471
472 function onSelectDdChange()
473 {
474     if (!submitted) return false;
475     resetPage();
476     loadSelect();
477     my_paz.show(0, recPerPage, m_sort);
478     return false;
479 }
480
481 function resetPage()
482 {
483     curPage = 1;
484     totalRec = 0;
485 }
486
487 function triggerSearch (query, sort, targets, windowid)
488 {
489     var pp2filter = "";
490     var pp2limit = "";
491
492     // Re-use previous query/sort if new ones are not specified
493     if (query) {
494         mkws.query = query;
495     }
496     if (sort) {
497         m_sort = sort;
498     }
499     if (targets) {
500         // ### should support multiple |-separated targets
501         m_filters.push({ id: targets, name: targets });
502     }
503
504     for (var i in m_filters) {
505         var filter = m_filters[i];
506         if (filter.id) {
507             if (pp2filter)
508                 pp2filter += ",";
509             if (filter.id.match(/^[a-z:]+[=~]/)) {
510                 debug("filter '" + filter.id + "' already begins with SETTING OP");
511             } else {
512                 filter.id = 'pz:id=' + filter.id;
513             }
514             pp2filter += filter.id;
515         } else {
516             if (pp2limit)
517                 pp2limit += ",";
518             pp2limit += filter.field + "=" + filter.value.replace(/[\\|,]/g, '\\$&');
519         }
520     }
521
522     var params = {};
523     if (pp2limit) {
524         params.limit = pp2limit;
525     }
526     if (windowid) {
527         params.windowid = windowid;
528     }
529     debug("triggerSearch(" + mkws.query + "): filters = " + $.toJSON(m_filters) + ", pp2filter = " + pp2filter + ", params = " + $.toJSON(params));
530
531     my_paz.search(mkws.query, recPerPage, m_sort, pp2filter, undefined, params);
532 }
533
534 function loadSelect ()
535 {
536     if (document.mkwsSelect) {
537         if (document.mkwsSelect.mkwsSort)
538             m_sort = document.mkwsSelect.mkwsSort.value;
539         if (document.mkwsSelect.mkwsPerpage)
540             recPerPage = document.mkwsSelect.mkwsPerpage.value;
541     }
542 }
543
544 // limit the query after clicking the facet
545 mkws.limitQuery = function (field, value)
546 {
547     debug("limitQuery(field=" + field + ", value=" + value + ")");
548     m_filters.push({ field: field, value: value });
549     redraw_navi();
550     resetPage();
551     loadSelect();
552     triggerSearch();
553     return false;
554 }
555
556 // limit by target functions
557 mkws.limitTarget  = function (id, name)
558 {
559     debug("limitTarget(id=" + id + ", name=" + name + ")");
560     m_filters.push({ id: id, name: name });
561     redraw_navi();
562     resetPage();
563     loadSelect();
564     triggerSearch();
565     return false;
566 }
567
568 mkws.delimitQuery = function (field, value)
569 {
570     debug("delimitQuery(field=" + field + ", value=" + value + ")");
571     var newFilters = [];
572     for (var i in m_filters) {
573         var filter = m_filters[i];
574         if (filter.field &&
575             field == filter.field &&
576             value == filter.value) {
577             debug("delimitTarget() removing filter " + $.toJSON(filter));
578         } else {
579             debug("delimitTarget() keeping filter " + $.toJSON(filter));
580             newFilters.push(filter);
581         }
582     }
583     m_filters = newFilters;
584
585     redraw_navi();
586     resetPage();
587     loadSelect();
588     triggerSearch();
589     return false;
590 }
591
592
593 mkws.delimitTarget = function (id)
594 {
595     debug("delimitTarget(id=" + id + ")");
596     var newFilters = [];
597     for (var i in m_filters) {
598         var filter = m_filters[i];
599         if (filter.id) {
600             debug("delimitTarget() removing filter " + $.toJSON(filter));
601         } else {
602             debug("delimitTarget() keeping filter " + $.toJSON(filter));
603             newFilters.push(filter);
604         }
605     }
606     m_filters = newFilters;
607
608     redraw_navi();
609     resetPage();
610     loadSelect();
611     triggerSearch();
612     return false;
613 }
614
615
616 function redraw_navi ()
617 {
618     var navi = document.getElementById('mkwsNavi');
619     if (!navi) return;
620
621     var text = "";
622     for (var i in m_filters) {
623         if (text) {
624             text += " | ";
625         }
626         var filter = m_filters[i];
627         if (filter.id) {
628             text += 'Source: <a class="crossout" href="#" onclick="mkws.delimitTarget(' +
629                 "'" + filter.id + "'" + ');return false;">' + filter.name + '</a>';
630         } else {
631             text += filter.field + ': <a class="crossout" href="#" onclick="mkws.delimitQuery(' +
632                 "'" + filter.field + "', '" + filter.value + "'" +
633                 ');return false;">' + filter.value + '</a>';
634         }
635     }
636
637     navi.innerHTML = text;
638 }
639
640
641 function drawPager (pagerDiv)
642 {
643     //client indexes pages from 1 but pz2 from 0
644     var onsides = 6;
645     var pages = Math.ceil(totalRec / recPerPage);
646
647     var firstClkbl = ( curPage - onsides > 0 )
648         ? curPage - onsides
649         : 1;
650
651     var lastClkbl = firstClkbl + 2*onsides < pages
652         ? firstClkbl + 2*onsides
653         : pages;
654
655     var prev = '<span id="mkwsPrev">&#60;&#60; ' + M('Prev') + '</span><b> | </b>';
656     if (curPage > 1)
657         prev = '<a href="#" id="mkwsPrev" onclick="mkws.pagerPrev();">'
658         +'&#60;&#60; ' + M('Prev') + '</a><b> | </b>';
659
660     var middle = '';
661     for(var i = firstClkbl; i <= lastClkbl; i++) {
662         var numLabel = i;
663         if(i == curPage)
664             numLabel = '<b>' + i + '</b>';
665
666         middle += '<a href="#" onclick="mkws.showPage(' + i + ')"> '
667             + numLabel + ' </a>';
668     }
669
670     var next = '<b> | </b><span id="mkwsNext">' + M('Next') + ' &#62;&#62;</span>';
671     if (pages - curPage > 0)
672         next = '<b> | </b><a href="#" id="mkwsNext" onclick="mkws.pagerNext()">'
673         + M('Next') + ' &#62;&#62;</a>';
674
675     var predots = '';
676     if (firstClkbl > 1)
677         predots = '...';
678
679     var postdots = '';
680     if (lastClkbl < pages)
681         postdots = '...';
682
683     pagerDiv.innerHTML += '<div style="float: clear">'
684         + prev + predots + middle + postdots + next + '</div>';
685 }
686
687 mkws.showPage = function (pageNum)
688 {
689     curPage = pageNum;
690     my_paz.showPage( curPage - 1 );
691 }
692
693 // simple paging functions
694
695 mkws.pagerNext = function () {
696     if ( totalRec - recPerPage*curPage > 0) {
697         my_paz.showNext();
698         curPage++;
699     }
700 }
701
702 mkws.pagerPrev = function () {
703     if ( my_paz.showPrev() != false )
704         curPage--;
705 }
706
707 // switching view between targets and records
708
709 mkws.switchView = function(view) {
710     debug("switchView: " + view);
711
712     var targets = document.getElementById('mkwsTargets');
713     var results = document.getElementById('mkwsResults') ||
714                   document.getElementById('mkwsRecords');
715     var blanket = document.getElementById('mkwsBlanket');
716     var motd    = document.getElementById('mkwsMOTD');
717
718     switch(view) {
719         case 'targets':
720             if (targets) targets.style.display = "block";
721             if (results) results.style.display = "none";
722             if (blanket) blanket.style.display = "none";
723             if (motd) motd.style.display = "none";
724             break;
725         case 'records':
726             if (targets) targets.style.display = "none";
727             if (results) results.style.display = "block";
728             if (blanket) blanket.style.display = "block";
729             if (motd) motd.style.display = "none";
730             break;
731         case 'none':
732             if (targets) targets.style.display = "none";
733             if (results) results.style.display = "none";
734             if (blanket) blanket.style.display = "none";
735             if (motd) motd.style.display = "none";
736             break;
737         default:
738             alert("Unknown view '" + view + "'");
739     }
740 }
741
742 // detailed record drawing
743 mkws.showDetails = function (prefixRecId) {
744     var recId = prefixRecId.replace('mkwsRec_', '');
745     var oldRecId = curDetRecId;
746     curDetRecId = recId;
747
748     // remove current detailed view if any
749     var detRecordDiv = document.getElementById('mkwsDet_'+oldRecId);
750     // lovin DOM!
751     if (detRecordDiv)
752       detRecordDiv.parentNode.removeChild(detRecordDiv);
753
754     // if the same clicked, just hide
755     if (recId == oldRecId) {
756         curDetRecId = '';
757         curDetRecData = null;
758         return;
759     }
760     // request the record
761     my_paz.record(recId);
762 }
763
764 function replaceHtml(el, html) {
765   var oldEl = typeof el === "string" ? document.getElementById(el) : el;
766   /*@cc_on // Pure innerHTML is slightly faster in IE
767     oldEl.innerHTML = html;
768     return oldEl;
769     @*/
770   var newEl = oldEl.cloneNode(false);
771   newEl.innerHTML = html;
772   oldEl.parentNode.replaceChild(newEl, oldEl);
773   /* Since we just removed the old element from the DOM, return a reference
774      to the new element, which can be used to restore variable references. */
775   return newEl;
776 };
777
778 function renderDetails(data, marker)
779 {
780     if (mkws.templateRecord === undefined) {
781         loadTemplate("Record");
782     }
783
784     var template = mkws.templateRecord;
785     var details = template(data);
786     return '<div class="details" id="mkwsDet_' + data.recid + '">' + details + '</div>';
787 }
788
789
790 function loadTemplate(name)
791 {
792     var source = $("#mkwsTemplate" + name).html();
793     if (!source) {
794         source = defaultTemplate(name);
795     }
796
797     var template = Handlebars.compile(source);
798     debug("compiled template '" + name + "'");
799     mkws['template' + name] = template;
800 }
801
802
803 function defaultTemplate(name)
804 {
805     if (name === 'Record') {
806         return '\
807       <table>\
808         <tr>\
809           <th>{{translate "Title"}}</th>\
810           <td>\
811             {{md-title}}\
812             {{#if md-title-remainder}}\
813               ({{md-title-remainder}})\
814             {{/if}}\
815             {{#if md-title-responsibility}}\
816               <i>{{md-title-responsibility}}</i>\
817             {{/if}}\
818           </td>\
819         </tr>\
820         {{#if md-date}}\
821         <tr>\
822           <th>{{translate "Date"}}</th>\
823           <td>{{md-date}}</td>\
824         </tr>\
825         {{/if}}\
826         {{#if md-author}}\
827         <tr>\
828           <th>{{translate "Author"}}</th>\
829           <td>{{md-author}}</td>\
830         </tr>\
831         {{/if}}\
832         {{#if md-electronic-url}}\
833         <tr>\
834           <th>{{translate "URL"}}</th>\
835           <td>\
836             {{#each md-electronic-url}}\
837               <a href="{{this}}">{{this}}</a><br/>\
838             {{/each}}\
839           </td>\
840         </tr>\
841         {{/if}}\
842         {{#if-any location having="md-subject"}}\
843         <tr>\
844           <th>{{translate "Subject"}}</th>\
845           <td>\
846             {{#first location having="md-subject"}}\
847               {{#if md-subject}}\
848                 {{md-subject}}\
849               {{/if}}\
850             {{/first}}\
851           </td>\
852         </tr>\
853         {{/if-any}}\
854         <tr>\
855           <th>{{translate "Locations"}}</th>\
856           <td>\
857             {{#commaList location}}\
858               {{attr "@name"}}{{/commaList}}\
859           </td>\
860         </tr>\
861       </table>\
862 ';
863     } else if (name === "Summary") {
864         return '\
865       <a href="#" id="{{_id}}" onclick="{{_onclick}}">\
866         <b>{{md-title}}</b>\
867       </a>\
868       {{#if md-title-remainder}}\
869         <span>{{md-title-remainder}}</span>\
870       {{/if}}\
871       {{#if md-title-responsibility}}\
872         <span><i>{{md-title-responsibility}}</i></span>\
873       {{/if}}\
874 ';
875     }
876
877     var s = "There is no default '" + name +"' template!";
878     alert(s);
879     return s;
880 }
881
882
883 /*
884  * All the HTML stuff to render the search forms and
885  * result pages.
886  */
887 function mkws_html_all() {
888     mkws_set_lang();
889     if (mkws_config.show_lang)
890         mkws_html_lang();
891
892     // For some reason, doing this programmatically results in
893     // document.mkwsSearchForm.mkwsQuery being undefined, hence the raw HTML.
894     debug("HTML search form");
895     $("#mkwsSearch").html('\
896     <form name="mkwsSearchForm" action="" >\
897       <input id="mkwsQuery" type="text" size="' + mkws_config.query_width + '" />\
898       <input id="mkwsButton" type="submit" value="' + M('Search') + '" />\
899     </form>');
900
901     debug("HTML records");
902     // If the application has an #mkwsResults, populate it in the
903     // usual way. If not, assume that it's a smarter application that
904     // defines its own subcomponents:
905     //  #mkwsTermlists
906     //  #mkwsRanking
907     //  #mkwsPager
908     //  #mkwsNavi
909     //  #mkwsRecords
910     if ($("#mkwsResults").length) {
911         $("#mkwsResults").html('\
912       <table width="100%" border="0" cellpadding="6" cellspacing="0">\
913         <tr>\
914           <td id="mkwsTermlistContainer1" width="250" valign="top">\
915             <div id="mkwsTermlists"></div>\
916           </td>\
917           <td id="mkwsMOTDContainer" valign="top">\
918             <div id="mkwsRanking"></div>\
919             <div id="mkwsPager"></div>\
920             <div id="mkwsNavi"></div>\
921             <div id="mkwsRecords"></div>\
922           </td>\
923         </tr>\
924         <tr>\
925           <td colspan="2">\
926             <div id="mkwsTermlistContainer2"></div>\
927           </td>\
928         </tr>\
929       </table>');
930     }
931
932     if ($("#mkwsRanking").length) {
933         var ranking_data = '';
934         ranking_data += '<form name="mkwsSelect" id="mkwsSelect" action="" >';
935         if (mkws_config.show_sort) {
936             ranking_data +=  M('Sort by') + ' ' + mkws_html_sort() + ' ';
937         }
938         if (mkws_config.show_perpage) {
939             ranking_data += M('and show') + ' ' + mkws_html_perpage() + ' ' + M('per page') + '.';
940         }
941         ranking_data += '</form>';
942
943         $("#mkwsRanking").html(ranking_data);
944     }
945
946     mkws_html_switch();
947
948     if (mkws_config.use_service_proxy) {
949           mkws_service_proxy_auth(mkws_config.service_proxy_auth,
950                                   mkws_config.service_proxy_auth_domain,
951                                   mkws_config.pazpar2_url);
952     } else {
953         // raw pp2
954         run_auto_searches();
955     }
956
957     if (mkws_config.responsive_design_width) {
958         // Responsive web design - change layout on the fly based on
959         // current screen width. Required for mobile devices.
960         $(window).resize( function(e) { mkws_resize_page() });
961         // initial check after page load
962         $(document).ready(function() { mkws_resize_page() });
963     }
964
965     domReady();
966
967     // on first page, hide the termlist
968     $(document).ready(function() { $("#mkwsTermlists").hide(); } );
969     var motd = document.getElementById("mkwsMOTD");
970     var container = document.getElementById("mkwsMOTDContainer");
971     if (motd && container) {
972         // Move the MOTD from the provided element down into the container
973         motd.parentNode.removeChild(motd);
974         container.appendChild(motd);
975     }
976 }
977
978
979 function run_auto_searches() {
980     debug("running auto searches");
981
982     $('[id^="mkwsRecords"]').each(function () {
983         var node = $(this);
984         var query = node.attr('autosearch');
985
986         if (query) {
987             var windowid = undefined;
988             var id = node.attr('id');
989             if (id.match(/^mkwsRecords_/, '')) {
990                 windowid = id.replace(/^mkwsRecords_/, '');
991             }
992
993             var sort = node.attr('sort');
994             var targets = node.attr('targets');
995             var s = "running auto search: '" + query + "'";
996             if (windowid) s += " [windowid '" + windowid + "']";
997             if (sort) s += " sorted by '" + sort + "'";
998             if (targets) s += " in targets '" + targets + "'";
999             debug(s);
1000             newSearch(query, sort, targets, windowid);
1001         }
1002     });
1003 }
1004
1005
1006 // implement $.parseQuerystring() for parsing URL parameters
1007 function parseQuerystring() {
1008     var nvpair = {};
1009     var qs = window.location.search.replace('?', '');
1010     var pairs = qs.split('&');
1011     $.each(pairs, function(i, v){
1012         var pair = v.split('=');
1013         nvpair[pair[0]] = pair[1];
1014     });
1015     return nvpair;
1016 }
1017
1018 function mkws_set_lang()  {
1019     var lang = parseQuerystring().lang || mkws_config.lang;
1020     if (!lang || !mkws.locale_lang[lang]) {
1021         mkws_config.lang = ""
1022     } else {
1023         mkws_config.lang = lang;
1024     }
1025
1026     debug("Locale language: " + (mkws_config.lang ? mkws_config.lang : "none"));
1027     return mkws_config.lang;
1028 }
1029
1030 function mkws_html_switch() {
1031     debug("HTML switch");
1032
1033     $("#mkwsSwitch").append($('<a href="#" id="mkwsSwitch_records" onclick="mkws.switchView(\'records\')">' + M('Records') + '</a>'));
1034     $("#mkwsSwitch").append($("<span/>", { text: " | " }));
1035     $("#mkwsSwitch").append($('<a href="#" id="mkwsSwitch_targets" onclick="mkws.switchView(\'targets\')">' + M('Targets') + '</a>'));
1036
1037     debug("HTML targets");
1038     $("#mkwsTargets").html('\
1039       <div id="mkwsBytarget">\
1040        No information available yet.\
1041       </div>');
1042     $("#mkwsTargets").css("display", "none");
1043 }
1044
1045 function mkws_html_sort() {
1046     debug("HTML sort, m_sort = '" + m_sort + "'");
1047     var sort_html = '<select name="mkwsSort" id="mkwsSort">';
1048
1049     for(var i = 0; i < mkws_config.sort_options.length; i++) {
1050         var opt = mkws_config.sort_options[i];
1051         var key = opt[0];
1052         var val = opt.length == 1 ? opt[0] : opt[1];
1053
1054         sort_html += '<option value="' + key + '"';
1055         if (m_sort == key || m_sort == val) {
1056             sort_html += ' selected="selected"';
1057         }
1058         sort_html += '>' + M(val) + '</option>';
1059     }
1060     sort_html += '</select>';
1061
1062     return sort_html;
1063 }
1064
1065 function mkws_html_perpage() {
1066     debug("HTML perpage");
1067     var perpage_html = '<select name="mkwsPerpage" id="mkwsPerpage">';
1068
1069     for(var i = 0; i < mkws_config.perpage_options.length; i++) {
1070         var key = mkws_config.perpage_options[i];
1071
1072         perpage_html += '<option value="' + key + '"';
1073         if (key == mkws_config.perpage_default) {
1074             perpage_html += ' selected="selected"';
1075         }
1076         perpage_html += '>' + key + '</option>';
1077     }
1078     perpage_html += '</select>';
1079
1080     return perpage_html;
1081 }
1082
1083 /*
1084  * Run service-proxy authentication in background (after page load).
1085  * The username/password is configured in the apache config file
1086  * for the site.
1087  */
1088 function mkws_service_proxy_auth(auth_url, auth_domain, pp2_url) {
1089     debug("Run service proxy auth URL: " + auth_url);
1090
1091     if (!auth_domain) {
1092         auth_domain = pp2_url.replace(/^(https?:)?\/\/(.*?)\/.*/, '$2');
1093         debug("guessed auth_domain '" + auth_domain + "' from pp2_url '" + pp2_url + "'");
1094     }
1095
1096     var request = new pzHttpRequest(auth_url, function(err) {
1097           alert("HTTP call for authentication failed: " + err)
1098           return;
1099     }, auth_domain);
1100
1101     request.get(null, function(data) {
1102         if (!$.isXMLDoc(data)) {
1103             alert("service proxy auth response document is not valid XML document, give up!");
1104             return;
1105         }
1106         var status = $(data).find("status");
1107         if (status.text() != "OK") {
1108             alert("service proxy auth repsonse status: " + status.text() + ", give up!");
1109             return;
1110         }
1111
1112         debug("Service proxy auth successfully done");
1113         mkws.authenticated = true;
1114         run_auto_searches();
1115     });
1116 }
1117
1118 /* create locale language menu */
1119 function mkws_html_lang() {
1120     var lang_default = "en";
1121     var lang = mkws_config.lang || lang_default;
1122     var list = [];
1123
1124     /* display a list of configured languages, or all */
1125     var lang_options = mkws_config.lang_options || [];
1126     var hash = {};
1127     for (var i = 0; i < lang_options.length; i++) {
1128         hash[lang_options[i]] = 1;
1129     }
1130
1131     for (var k in mkws.locale_lang) {
1132         if (hash[k] == 1 || lang_options.length == 0)
1133             list.push(k);
1134     }
1135
1136     // add english link
1137     if (lang_options.length == 0 || hash[lang_default] == 1)
1138         list.push(lang_default);
1139
1140     debug("Language menu for: " + list.join(", "));
1141
1142     /* the HTML part */
1143     var data = "";
1144     for(var i = 0; i < list.length; i++) {
1145         var l = list[i];
1146
1147         if (data)
1148             data += ' | ';
1149
1150         if (lang == l) {
1151             data += ' <span>' + l + '</span> ';
1152         } else {
1153             data += ' <a href="?lang=' + l + '">' + l + '</a> '
1154         }
1155     }
1156
1157     $("#mkwsLang").html(data);
1158 }
1159
1160 function mkws_resize_page () {
1161     var list = ["mkwsSwitch"];
1162
1163     var width = mkws_config.responsive_design_width;
1164     var parentId = $("#mkwsTermlists").parent().attr('id');
1165
1166     if ($(window).width() <= width &&
1167         parentId === "mkwsTermlistContainer1") {
1168         debug("changing from wide to narrow: " + $(window).width());
1169         $("#mkwsTermlists").appendTo($("#mkwsTermlistContainer2"));
1170         $("#mkwsTermlistContainer1").hide();
1171         $("#mkwsTermlistContainer2").show();
1172         for(var i = 0; i < list.length; i++) {
1173             $("#" + list[i]).hide();
1174         }
1175     } else if ($(window).width() > width &&
1176         parentId === "mkwsTermlistContainer2") {
1177         debug("changing from narrow to wide: " + $(window).width());
1178         $("#mkwsTermlists").appendTo($("#mkwsTermlistContainer1"));
1179         $("#mkwsTermlistContainer1").show();
1180         $("#mkwsTermlistContainer2").hide();
1181         for(var i = 0; i < list.length; i++) {
1182             $("#" + list[i]).show();
1183         }
1184     }
1185 };
1186
1187 /* locale */
1188 function M(word) {
1189     var lang = mkws_config.lang;
1190
1191     if (!lang || !mkws.locale_lang[lang])
1192         return word;
1193
1194     return mkws.locale_lang[lang][word] || word;
1195 }
1196
1197 // main
1198 (function() {
1199     try {
1200         mkws_html_all()
1201     }
1202
1203     catch (e) {
1204         mkws_config.error = e.message;
1205         // alert(e.message);
1206     }
1207 })();
1208
1209     // done
1210     mkws.init = true;
1211 };
1212
1213
1214 /*
1215  * implement jQuery plugin $.pazpar2({})
1216  */
1217 function _mkws_jquery_plugin ($) {
1218     var debug_level = 1;
1219
1220     function debug (string) {
1221         if (!debug_level)
1222             return;
1223
1224         if (typeof console === "undefined" || typeof console.log === "undefined")
1225             return;
1226
1227         console.log("jquery.pazpar2: " + string);
1228     }
1229
1230     function init_popup(obj) {
1231         var config = obj ? obj : {};
1232
1233         var height = config.height || 760;
1234         var width = config.width || 880;
1235         var id_button = config.id_button || "input#mkwsButton";
1236         var id_popup = config.id_popup || "#mkwsPopup";
1237
1238         debug("popup height: " + height + ", width: " + width);
1239
1240         // make sure that jquery-ui was loaded afte jQuery core lib, e.g.:
1241         // <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
1242         if (!$.ui) {
1243             debug("Error: jquery-ui.js is missing, did you include it after jQuery core in the HTML file?");
1244             return;
1245         }
1246
1247         $(id_popup).dialog({
1248           closeOnEscape: true,
1249           autoOpen: false,
1250           height: height,
1251           width: width,
1252           modal: true,
1253           resizable: true,
1254           buttons: {
1255                   Cancel: function() {
1256                           $(this).dialog("close");
1257                   }
1258           },
1259           close: function() { }
1260         });
1261
1262         $(id_button)
1263           .button()
1264           .click(function() {
1265                   $(id_popup).dialog("open");
1266           });
1267     };
1268
1269     $.extend({
1270
1271     // service-proxy or pazpar2
1272     pazpar2: function(config) {
1273         var id_popup = config.id_popup || "#mkwsPopup";
1274         id_popup = id_popup.replace(/^#/, "");
1275
1276         // simple layout
1277         var div = '<div id="mkwsSwitch"></div>\
1278         <div id="mkwsLang"></div>\
1279         <div id="mkwsSearch"></div>\
1280         <div id="mkwsResults"></div>\
1281         <div id="mkwsTargets"></div>\
1282         <div id="mkwsStat"></div>';
1283
1284         // new table layout
1285         var table = '\
1286         <style type="text/css">\
1287           #mkwsTermlists div.facet {\
1288           float:left;\
1289           width: 30%;\
1290           margin: 0.3em;\
1291           }\
1292           #mkwsStat {\
1293           text-align: right;\
1294           }\
1295         </style>\
1296             \
1297         <table width="100%" border="0">\
1298           <tr>\
1299             <td>\
1300               <div id="mkwsSwitch"></div>\
1301               <div id="mkwsLang"></div>\
1302               <div id="mkwsSearch"></div>\
1303             </td>\
1304           </tr>\
1305           <tr>\
1306             <td>\
1307               <div style="height:500px; overflow: auto">\
1308                 <div id="mkwsPager"></div>\
1309                 <div id="mkwsNavi"></div>\
1310                 <div id="mkwsRecords"></div>\
1311                 <div id="mkwsTargets"></div>\
1312                 <div id="mkwsRanking"></div>\
1313               </div>\
1314             </td>\
1315           </tr>\
1316           <tr>\
1317             <td>\
1318               <div style="height:300px; overflow: hidden">\
1319                 <div id="mkwsTermlists"></div>\
1320               </div>\
1321             </td>\
1322           </tr>\
1323           <tr>\
1324             <td>\
1325               <div id="mkwsStat"></div>\
1326             </td>\
1327           </tr>\
1328         </table>';
1329
1330         var popup = '\
1331           <div id="mkwsSearch"></div>\
1332           <div id="' + id_popup + '">\
1333             <div id="mkwsSwitch"></div>\
1334             <div id="mkwsLang"></div>\
1335             <div id="mkwsResults"></div>\
1336             <div id="mkwsTargets"></div>\
1337             <div id="mkwsStat"></div>\
1338           </div>'
1339
1340         if (config && config.layout == 'div') {
1341             debug("jquery plugin layout: div");
1342             document.write(div);
1343         } else if (config && config.layout == 'popup') {
1344             debug("jquery plugin layout: popup with id: " + id_popup);
1345             document.write(popup);
1346             $(document).ready( function() { init_popup(config); } );
1347         } else {
1348             debug("jquery plugin layout: table");
1349             document.write(table);
1350         }
1351     }
1352 });
1353 };
1354
1355 // wrapper to call _make_mkws_team() after page load
1356 (function (j) {
1357     // enable before page load, so we could call it before mkws() runs
1358     _mkws_jquery_plugin(j);
1359
1360     $(document).ready(function() {
1361         // if (console && console.log) console.log("on load ready");
1362         _make_mkws_team(j, null);
1363     });
1364 })(jQuery);