Remove explicit windowid parameter from newSearch() and triggerSearch()
[mkws-moved-to-github.git] / tools / htdocs / mkws.js
index b9f13ef..e47dc82 100644 (file)
@@ -142,12 +142,50 @@ var mkws = {
 };
 
 
+// The following PubSub code is modified from the jQuery manual:
+// https://api.jquery.com/jQuery.Callbacks/
+//
+// Use as:
+//     mkws.queue("eventName").subscribe(function(param1, param2 ...) { ... });
+//     mkws.queue("eventName").publish(arg1, arg2, ...);
+
+(function() {
+  var queues = {};
+  mkws.queue = function(id) {
+    if (!queues[id]) {
+      var callbacks = $.Callbacks();
+      queues[id] = {
+       publish: callbacks.fire,
+       subscribe: callbacks.add,
+       unsubscribe: callbacks.remove
+      };
+    }
+    return queues[id];
+  }
+}());
+
+
 // Define empty mkws_config for simple applications that don't define it.
 if (mkws_config == null || typeof mkws_config != 'object') {
     var mkws_config = {};
 }
 
 
+// Factory function for widget objects.
+function widget($, team, type, node) {
+    var that = {
+       team: team,
+       type: type,
+       node: node
+    };
+
+    // ### More to do here, surely: e.g. wiring into the team
+    mkws.debug("made widget(team=" + team + ", type=" + type + ", node=" + node);
+
+    return that;
+}
+
+
 // Factory function for team objects. As much as possible, this uses
 // only member variables (prefixed "m_") and inner functions with
 // private scope. Some functions are visibl as member-functions to be
@@ -174,6 +212,7 @@ function team($, teamName) {
        "last": $.now()
     };
     var m_paz; // will be initialised below
+    var m_template = {};
 
 
     var debug = function (s) {
@@ -189,22 +228,6 @@ function team($, teamName) {
     m_sort = mkws_config.sort_default;
     debug("copied mkws_config.sort_default '" + mkws_config.sort_default + "' to m_sort");
 
-    if (mkws_config.query_width < 5 || mkws_config.query_width > 150) {
-       debug("Reset query width: " + mkws_config.query_width);
-       mkws_config.query_width = 50;
-    }
-
-    for (var key in mkws_config) {
-       if (mkws_config.hasOwnProperty(key)) {
-           if (key.match(/^language_/)) {
-               var lang = key.replace(/^language_/, "");
-               // Copy custom languages into list
-               mkws.locale_lang[lang] = mkws_config[key];
-               debug("Added locally configured language '" + lang + "'");
-           }
-       }
-    }
-
     // protocol independent link for pazpar2: "//mkws/sp" -> "https://mkws/sp"
     if (mkws_config.pazpar2_url.match(/^\/\//)) {
        mkws_config.pazpar2_url = document.location.protocol + mkws_config.pazpar2_url;
@@ -233,6 +256,21 @@ function team($, teamName) {
     }
 
 
+    // Finds the node of the specified class within the current team
+    // Multiple OR-clauses separated by commas are handled
+    // More complex cases may not work
+    //
+    function findnode(selector, teamName) {
+       teamName = teamName || m_teamName;
+
+       selector = selector.split(',').map(function(s) {
+           return s + '.mkwsTeam_' + teamName;
+       }).join(',');
+
+       return $(selector);
+    }
+
+
     //
     // pz2.js event handlers:
     //
@@ -247,12 +285,12 @@ function team($, teamName) {
        debug("show");
        m_totalRec = data.merged;
 
-       var pager = $(".mkwsPager.mkwsTeam_" + m_teamName);
+       var pager = findnode(".mkwsPager");
        if (pager.length) {
            pager.html(drawPager(data))
        }
 
-       var results = $(".mkwsRecords.mkwsTeam_" + m_teamName);
+       var results = findnode(".mkwsRecords");
        if (!results.length)
            return;
 
@@ -282,7 +320,7 @@ function team($, teamName) {
 
     function my_onstat(data, teamName) {
        debug("stat");
-       var stat = $('.mkwsStat.mkwsTeam_' + teamName);
+       var stat = findnode('.mkwsStat');
        if (stat.length === 0)
            return;
 
@@ -296,7 +334,7 @@ function team($, teamName) {
 
     function my_onterm(data, teamName) {
        debug("term");
-       var node = $(".mkwsTermlists.mkwsTeam_" + teamName);
+       var node = findnode(".mkwsTermlists");
        if (node.length == 0) return;
 
        // no facets: this should never happen
@@ -380,7 +418,7 @@ function team($, teamName) {
 
     function my_onbytarget(data, teamName) {
        debug("target");
-       var targetDiv = $('.mkwsBytarget.mkwsTeam_' + teamName);
+       var targetDiv = findnode('.mkwsBytarget');
        if (!targetDiv) {
            return;
        }
@@ -412,16 +450,13 @@ function team($, teamName) {
     // when search button pressed
     function onFormSubmitEventHandler()
     {
-       mkws.handle_node_with_team(this, function (tname) {
-           var val = $('.mkwsQuery.mkwsTeam_' + tname).val();
-           mkws.teams[tname].newSearch(val);
-       });
-
+       var val = findnode('.mkwsQuery').val();
+       newSearch(val);
        return false;
     }
 
 
-    that.newSearch = function(query, sort, targets, windowid)
+    function newSearch(query, sort, targets)
     {
        debug("newSearch: " + query);
 
@@ -434,8 +469,8 @@ function team($, teamName) {
        redraw_navi();
        resetPage();
        loadSelect();
-       triggerSearch(query, sort, targets, windowid);
-       that.switchView('records'); // In case it's configured to start off as hidden
+       triggerSearch(query, sort, targets);
+       switchView('records'); // In case it's configured to start off as hidden
        m_submitted = true;
     }
 
@@ -457,7 +492,7 @@ function team($, teamName) {
     }
 
 
-    function triggerSearch (query, sort, targets, windowid)
+    function triggerSearch (query, sort, targets)
     {
        var pp2filter = "";
        var pp2limit = "";
@@ -496,23 +531,23 @@ function team($, teamName) {
        if (pp2limit) {
            params.limit = pp2limit;
        }
-       if (windowid) {
-           params.windowid = windowid;
-       }
+
        debug("triggerSearch(" + m_query + "): filters = " + $.toJSON(m_filters) + ", pp2filter = " + pp2filter + ", params = " + $.toJSON(params));
 
+       // We can use: params.torusquery = "udb=NAME"
+       // Note: that won't work when running against raw pazpar2
        m_paz.search(m_query, m_perpage, m_sort, pp2filter, undefined, params);
     }
 
 
     function loadSelect ()
     {
-       var node = $('.mkwsSort.mkwsTeam_' + m_teamName);
+       var node = findnode('.mkwsSort');
        if (node.length && node.val() != m_sort) {
            debug("changing m_sort from " + m_sort + " to " + node.val());
            m_sort = node.val();
        }
-       node = $('.mkwsPerpage.mkwsTeam_' + m_teamName);
+       node = findnode('.mkwsPerpage');
        if (node.length && node.val() != m_perpage) {
            debug("changing m_perpage from " + m_perpage + " to " + node.val());
            m_perpage = node.val();
@@ -596,7 +631,7 @@ function team($, teamName) {
 
     function redraw_navi ()
     {
-       var navi = $('.mkwsNavi.mkwsTeam_' + m_teamName);
+       var navi = findnode('.mkwsNavi');
        if (!navi) return;
 
        var text = "";
@@ -696,11 +731,11 @@ function team($, teamName) {
 
 
     // switching view between targets and records
-    that.switchView = function(view) {
-       var targets = $('.mkwsTargets.mkwsTeam_' + m_teamName);
-       var results = $('.mkwsResults.mkwsTeam_' + m_teamName + ',.mkwsRecords.mkwsTeam_' + m_teamName);
-       var blanket = $('.mkwsBlanket.mkwsTeam_' + m_teamName);
-       var motd    = $('.mkwsMOTD.mkwsTeam_' + m_teamName);
+    function switchView(view) {
+       var targets = findnode('.mkwsTargets');
+       var results = findnode('.mkwsResults,.mkwsRecords');
+       var blanket = findnode('.mkwsBlanket');
+       var motd    = findnode('.mkwsMOTD');
 
        switch(view) {
         case 'targets':
@@ -728,6 +763,9 @@ function team($, teamName) {
     }
 
 
+    that.switchView = switchView;
+
+
     // detailed record drawing
     that.showDetails = function (prefixRecId) {
        var recId = prefixRecId.replace('mkwsRec_', '');
@@ -762,17 +800,23 @@ function team($, teamName) {
 
     function loadTemplate(name)
     {
-       var template = mkws['template' + name];
+       var template = m_template[name];
 
        if (template === undefined) {
-           var source = $("#mkwsTemplate" + name).html();
+           // Fall back to generic template if there is no team-specific one
+           var node = findnode(".mkwsTemplate_" + name);
+           if (!node.length) {
+               node = findnode(".mkwsTemplate_" + name, "ALL");
+           }
+
+           var source = node.html();
            if (!source) {
                source = defaultTemplate(name);
            }
 
            template = Handlebars.compile(source);
            debug("compiled template '" + name + "'");
-           mkws['template' + name] = template;
+           m_template[name] = template;
        }
 
        return template;
@@ -864,22 +908,19 @@ function team($, teamName) {
      * All the HTML stuff to render the search forms and
      * result pages.
      */
+    // ### This and other multi-word identifiers should be camelCase
     function mkws_html_all() {
        mkws_set_lang();
        if (mkws_config.show_lang)
            mkws_html_lang();
 
        debug("HTML search form");
-       // ### There is only one match here by design: fix not to bother looping
-       $('.mkwsSearch.mkwsTeam_' + m_teamName).each(function (i, obj) {
-           var node = this;
-           mkws.handle_node_with_team(node, function(tname) {
-               $(node).html('\
+       mkws.handle_node_with_team(findnode('.mkwsSearch'), function(tname) {
+           this.html('\
 <form name="mkwsSearchForm" class="mkwsSearchForm mkwsTeam_' + tname + '" action="" >\
   <input class="mkwsQuery mkwsTeam_' + tname + '" type="text" size="' + mkws_config.query_width + '" />\
   <input class="mkwsButton mkwsTeam_' + tname + '" type="submit" value="' + M('Search') + '" />\
 </form>');
-           });
        });
 
        debug("HTML records");
@@ -892,8 +933,8 @@ function team($, teamName) {
        //      .mkwsPager
        //      .mkwsNavi
        //      .mkwsRecords
-       if ($(".mkwsResults.mkwsTeam_" + m_teamName).length) {
-           $(".mkwsResults.mkwsTeam_" + m_teamName).html('\
+       if (findnode(".mkwsResults").length) {
+           findnode(".mkwsResults").html('\
 <table width="100%" border="0" cellpadding="6" cellspacing="0">\
   <tr>\
     <td class="mkwsTermlistContainer1 mkwsTeam_' + m_teamName + '" width="250" valign="top">\
@@ -914,7 +955,7 @@ function team($, teamName) {
 </table>');
        }
 
-       var node = $(".mkwsRanking.mkwsTeam_" + m_teamName);
+       var node = findnode(".mkwsRanking");
        if (node.length) {
            var ranking_data = '';
            ranking_data += '<form name="mkwsSelect" class="mkwsSelect mkwsTeam_' + m_teamName + '" action="" >';
@@ -940,26 +981,21 @@ function team($, teamName) {
            $(document).ready(function() { mkws.resize_page() });
        }
 
-       $('.mkwsSearchForm.mkwsTeam_' + m_teamName).each(function (i, obj) {
-           debug("adding search-forms for team '" + m_teamName + "'");
-           var node = this;
-           mkws.handle_node_with_team(node, function(tname) {
-               debug("adding search-form '" + tname + "' for team '" + m_teamName + "'");
-               $(node).submit(onFormSubmitEventHandler);
-           });
-       });
-
-       node = $('.mkwsSort.mkwsTeam_' + m_teamName);
+       var node;
+       node = findnode('.mkwsSearchForm');
+       if (node.length)
+           node.submit(onFormSubmitEventHandler);
+       node = findnode('.mkwsSort');
        if (node.length)
            node.change(onSelectDdChange);
-       node = $('.mkwsPerpage.mkwsTeam_' + m_teamName);
+       node = findnode('.mkwsPerpage');
        if (node.length)
            node.change(onSelectDdChange);
 
        // on first page, hide the termlist
-       $(document).ready(function() { $(".mkwsTermlists.mkwsTeam_" + m_teamName).hide(); });
-       var motd = $(".mkwsMOTD.mkwsTeam_" + m_teamName);
-       var container = $(".mkwsMOTDContainer.mkwsTeam_" + m_teamName);
+       $(document).ready(function() { findnode(".mkwsTermlists").hide(); });
+       var motd = findnode(".mkwsMOTD");
+        var container = findnode(".mkwsMOTDContainer");
        if (motd.length && container.length) {
            // Move the MOTD from the provided element down into the container
            motd.appendTo(container);
@@ -996,13 +1032,13 @@ function team($, teamName) {
     function mkws_html_switch() {
        debug("HTML switch for team " + m_teamName);
 
-       var node = $(".mkwsSwitch.mkwsTeam_" + m_teamName);
+       var node = findnode(".mkwsSwitch");
        node.append($('<a href="#" onclick="mkws.switchView(\'' + m_teamName + '\', \'records\')">' + M('Records') + '</a>'));
        node.append($("<span/>", { text: " | " }));
        node.append($('<a href="#" onclick="mkws.switchView(\'' + m_teamName + '\', \'targets\')">' + M('Targets') + '</a>'));
 
        debug("HTML targets");
-       var node = $(".mkwsTargets.mkwsTeam_" + m_teamName);
+       var node = findnode(".mkwsTargets");
        node.html('\
 <div class="mkwsBytarget mkwsTeam_' + m_teamName + '">\
   No information available yet.\
@@ -1090,7 +1126,55 @@ function team($, teamName) {
            }
        }
 
-       $(".mkwsLang.mkwsTeam_" + m_teamName).html(data);
+       findnode(".mkwsLang").html(data);
+    }
+
+
+    that.run_auto_search = function() {
+       // ### should check mkwsTermlist as well, for facet-only teams
+       var node = findnode('.mkwsRecords');
+       var query = node.attr('autosearch');
+       if (!query)
+           return;
+
+       if (query.match(/^!param!/)) {
+           var param = query.replace(/^!param!/, '');
+           query = getParameterByName(param);
+           debug("obtained query '" + query + "' from param '" + param + "'");
+           if (!query) {
+               alert("This page has a MasterKey widget that needs a query specified by the '" + param + "' parameter");
+           }
+       } else if (query.match(/^!path!/)) {
+           var index = query.replace(/^!path!/, '');
+           var path = window.location.pathname.split('/');
+           query = path[path.length - index];
+           debug("obtained query '" + query + "' from path-component '" + index + "'");
+           if (!query) {
+               alert("This page has a MasterKey widget that needs a query specified by the path-component " + index);
+           }
+       }
+
+       debug("node=" + node + ", class='" + node.className + "', query=" + query);
+
+       var sort = node.attr('sort');
+       var targets = node.attr('targets');
+       var s = "running auto search: '" + query + "'";
+       if (sort) s += " sorted by '" + sort + "'";
+       if (targets) s += " in targets '" + targets + "'";
+       debug(s);
+
+       newSearch(query, sort, targets);
+    }
+
+
+    // This function is taken from a StackOverflow answer
+    // http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript/901144#901144
+    // ### should we unify this and parseQuerystring()?
+    function getParameterByName(name) {
+       name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+       var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+           results = regex.exec(location.search);
+       return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
     }
 
 
@@ -1146,16 +1230,28 @@ function team($, teamName) {
 
 
     mkws.handle_node_with_team = function(node, callback) {
-       var classes = node.className;
+       // First branch for DOM objects; second branch for jQuery objects
+       var classes = node.className || node.attr('class');
+       if (!classes) {
+           // For some reason, if we try to proceed when classes is
+           // undefined, we don't get an error message, but this
+           // function and its callers, up several stack level,
+           // silently return. What a crock.
+           mkws.debug("handle_node_with_team() called on node with no classes");
+           return;
+       }
        var list = classes.split(/\s+/)
-       var tname;
+       var teamName, type;
+
        for (var i = 0; i < list.length; i++) {
            var cname = list[i];
            if (cname.match(/^mkwsTeam_/)) {
-               tname = cname.replace(/^mkwsTeam_/, '');
+               teamName = cname.replace(/^mkwsTeam_/, '');
+           } else if (cname.match(/^mkws/)) {
+               type = cname.replace(/^mkws/, '');
            }
        }
-       callback(tname);
+       callback.call(node, teamName, type);
     }
 
 
@@ -1313,64 +1409,36 @@ function team($, teamName) {
        debug("running auto searches");
 
        for (var teamName in mkws.teams) {
-           // ### should check mkwsTermlist as well, for facet-only teams
-           var node = $('.mkwsRecords.mkwsTeam_' + teamName);
-           var query = node.attr('autosearch');
-
-           if (query.match(/^!param!/)) {
-               var param = query.replace(/^!param!/, '');
-               query = getParameterByName(param);
-               debug("obtained query '" + query + "' from param '" + param + "'");
-               if (!query) {
-                   alert("This page has a MasterKey widget that needs a query specified by the '" + param + "' parameter");
-               }
-           } else if (query.match(/^!path!/)) {
-               var index = query.replace(/^!path!/, '');
-               var path = window.location.pathname.split('/');
-               query = path[path.length - index];
-               debug("obtained query '" + query + "' from path-component '" + index + "'");
-               if (!query) {
-                   alert("This page has a MasterKey widget that needs a query specified by the path-component " + index);
-               }
-           }
-
-           debug("teamName '" + teamName + "', node=" + node + ", class='" + node.className + "', query=" + query);
-
-           if (query) {
-               var sort = node.attr('sort');
-               var targets = node.attr('targets');
-               var s = "running auto search: '" + query + "'";
-               if (teamName) s += " [teamName '" + teamName + "']";
-               if (sort) s += " sorted by '" + sort + "'";
-               if (targets) s += " in targets '" + targets + "'";
-               debug(s);
-               var team = mkws.teams[teamName];
-               debug($.toJSON(team));
-               team.newSearch(query, sort, targets, teamName);
-           }
+           mkws.teams[teamName].run_auto_search();
        }
     }
 
 
-    // This function is taken from a StackOverflow answer
-    // http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript/901144#901144
-    function getParameterByName(name) {
-       name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
-       var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
-           results = regex.exec(location.search);
-       return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
-    }
-
-
     $(document).ready(function() {
        debug("on load ready");
        default_mkws_config();
 
+       if (mkws_config.query_width < 5 || mkws_config.query_width > 150) {
+           debug("Reset query width: " + mkws_config.query_width);
+           mkws_config.query_width = 50;
+       }
+
+       for (var key in mkws_config) {
+           if (mkws_config.hasOwnProperty(key)) {
+               if (key.match(/^language_/)) {
+                   var lang = key.replace(/^language_/, "");
+                   // Copy custom languages into list
+                   mkws.locale_lang[lang] = mkws_config[key];
+                   debug("Added locally configured language '" + lang + "'");
+               }
+           }
+       }
+
        // Backwards compatibility: set new magic class names on any
        // elements that have the old magic IDs.
        var ids = [ "Switch", "Lang", "Search", "Pager", "Navi",
                    "Results", "Records", "Targets", "Ranking",
-                   "Termlists", "Stat" ];
+                   "Termlists", "Stat", "MOTD" ];
        for (var i = 0; i < ids.length; i++) {
            var id = 'mkws' + ids[i];
            var node = $('#' + id);
@@ -1391,15 +1459,19 @@ function team($, teamName) {
 
        // Find all nodes with an class, and determine their team from
        // the mkwsTeam_* class. Make all team objects.
+       var then = $.now();
        $('[class^="mkws"],[class*=" mkws"]').each(function () {
-           var node = this;
-           mkws.handle_node_with_team(node, function(tname) {
+           mkws.handle_node_with_team(this, function(tname, type) {
                if (!mkws.teams[tname]) {
                    mkws.teams[tname] = team(j, tname);
                    debug("Made MKWS team '" + tname + "'");
                }
+               var myTeam = mkws.teams[tname];
+               var myWidget = widget(j, myTeam, type, this);
            });
        });
+       var now = $.now();
+       debug("Walking MKWS nodes took " + (now-then) + " ms");
 
        if (mkws_config.use_service_proxy) {
            authenticate_session(mkws_config.service_proxy_auth,