Move widget factory function and autosearch functionality out of
[mkws-moved-to-github.git] / src / mkws-widgets.js
index 0f58002..69d1360 100644 (file)
@@ -1,108 +1,3 @@
-// Factory function for widget objects.
-function widget($, team, type, node) {
-    // Static register of attributes that do not contribute to config
-    var ignoreAttrs = {
-       id:1, 'class':1, style:1, name:1, action:1, type:1, size:1,
-       value:1, width:1, valign:1
-    };
-
-    var that = {
-       team: team,
-       type: type,
-       node: node,
-       config: Object.create(team.config())
-    };
-
-    function log(s) {
-       team.log(s);
-    }
-    that.log = log;
-
-    that.toString = function() {
-       return '[Widget ' + team.name() + ':' + type + ']';
-    };
-
-    for (var i = 0; i < node.attributes.length; i++) {
-       var a = node.attributes[i];
-       if (a.name === 'data-mkws-config') {
-           // Treat as a JSON fragment configuring just this widget
-           log(node + ": parsing config fragment '" + a.value + "'");
-           var data;
-           try {
-               data = $.parseJSON(a.value);
-               for (var key in data) {
-                   log(node + ": adding config element " + key + "='" + data[key] + "'");
-                   that.config[key] = data[key];
-               }
-           } catch (err) {
-               alert("Can't parse " + node + " data-mkws-config as JSON: " + a.value);
-           }
-       } else if (a.name.match (/^data-mkws-/)) {
-           var name = a.name.replace(/^data-mkws-/, '')
-           that.config[name] = a.value;
-           log(node + ": set data-mkws attribute " + name + "='" + a.value + "'");
-       } else if (!ignoreAttrs[a.name]) {
-           that.config[a.name] = a.value;
-           log(node + ": set regular attribute " + a.name + "='" + a.value + "'");
-       }
-    }
-
-    var fn = mkws.promotionFunction(type);
-    if (fn) {
-       fn.call(that);
-       log("made " + type + " widget(node=" + node + ")");
-    } else {
-       log("made UNPROMOTED widget(type=" + type + ", node=" + node + ")");
-    }
-
-    return that;
-}
-
-
-// Utility function for use by all widgets that can invoke autosearch.
-widget.autosearch = function(widget) {
-    var query = widget.config.autosearch;
-    if (query) {
-       if (query.match(/^!param!/)) {
-           var param = query.replace(/^!param!/, '');
-           query = mkws.getParameterByName(param);
-           widget.log("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];
-           widget.log("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);
-           }
-       }
-
-       widget.team.queue("ready").subscribe(function() {
-           var sortOrder = widget.config.sort;
-           var maxrecs = widget.config.maxrecs;
-           var perpage = widget.config.perpage;
-           var limit = widget.config.limit;
-           var targets = widget.config.targets;
-           var targetfilter = widget.config.targetfilter;
-
-           var s = "running auto search: '" + query + "'";
-           if (sortOrder) s += " sorted by '" + sortOrder + "'";
-           if (maxrecs) s += " restricted to " + maxrecs + " records";
-           if (perpage) s += " with " + perpage + " per page";
-           if (limit) s += " limited by '" + limit + "'";
-           if (targets) s += " in targets '" + targets + "'";
-           if (targetfilter) s += " constrained by targetfilter '" + targetfilter + "'";
-           widget.log(s);
-
-           widget.team.newSearch(query, sortOrder, maxrecs, perpage, limit, targets, targetfilter);
-       });
-    }
-};
-
-
 // Functions follow for promoting the regular widget object into
 // widgets of specific types. These could be moved into their own
 // source files.
@@ -112,6 +7,12 @@ mkws.registerWidgetType('Targets', function() {
     var that = this;
     var M = mkws.M;
 
+    $(this.node).html('\
+<div class="mkwsBytarget mkwsTeam_' + this.team.name() + '">\
+No information available yet.\
+</div>');
+    $(this.node).css("display", "none");
+
     this.team.queue("targets").subscribe(function(data) {
        var table ='<table><thead><tr>' +
            '<td>' + M('Target ID') + '</td>' +
@@ -152,73 +53,6 @@ mkws.registerWidgetType('Stat', function() {
 });
 
 
-mkws.registerWidgetType('Termlists', function() {
-    var that = this;
-    var M = mkws.M;
-
-    this.team.queue("termlists").subscribe(function(data) {
-       if (!that.node) {
-           alert("termlists event when there are no termlists");
-           return;
-       }
-
-       // no facets: this should never happen
-       var facets = that.config.facets;
-       if (!facets || facets.length == 0) {
-           alert("onTerm called even though we have no facets: " + $.toJSON(data));
-           $(that.node).hide();
-           return;
-       }
-
-       // display if we first got results
-       $(that.node).show();
-
-       var acc = [];
-       acc.push('<div class="title">' + M('Termlists') + '</div>');
-
-       for (var i = 0; i < facets.length; i++) {
-           if (facets[i] == "xtargets") {
-               addSingleFacet(acc, "Sources",  data.xtargets, 16, null);
-           } else if (facets[i] == "subject") {
-               addSingleFacet(acc, "Subjects", data.subject,  10, "subject");
-           } else if (facets[i] == "author") {
-               addSingleFacet(acc, "Authors",  data.author,   10, "author");
-           } else {
-               alert("bad facet configuration: '" + facets[i] + "'");
-           }
-       }
-
-       $(that.node).html(acc.join(''));
-
-       function addSingleFacet(acc, caption, data, max, pzIndex) {
-           var teamName = that.team.name();
-           acc.push('<div class="facet mkwsFacet' + caption + ' mkwsTeam_' + teamName + '">');
-           acc.push('<div class="termtitle">' + M(caption) + '</div>');
-           for (var i = 0; i < data.length && i < max; i++) {
-               acc.push('<div class="term">');
-               acc.push('<a href="#" ');
-               var action = '';
-               if (!pzIndex) {
-                   // Special case: target selection
-                   acc.push('target_id='+data[i].id+' ');
-                   if (!that.team.targetFiltered(data[i].id)) {
-                       action = 'mkws.limitTarget(\'' + teamName + '\', this.getAttribute(\'target_id\'),this.firstChild.nodeValue)';
-                   }
-               } else {
-                   action = 'mkws.limitQuery(\'' + teamName + '\', \'' + pzIndex + '\', this.firstChild.nodeValue)';
-               }
-               acc.push('onclick="' + action + ';return false;">' + data[i].name + '</a>'
-                        + ' <span>' + data[i].freq + '</span>');
-               acc.push('</div>');
-           }
-           acc.push('</div>');
-       }
-    });
-
-    widget.autosearch(that);
-});
-
-
 mkws.registerWidgetType('Pager', function() {
     var that = this;
     var M = mkws.M;
@@ -246,24 +80,24 @@ mkws.registerWidgetType('Pager', function() {
                ? firstClkbl + 2*onsides
                : pages;
 
-           var prev = '<span class="mkwsPrev">&#60;&#60; ' + M('Prev') + '</span><b> | </b>';
+           var prev = '<span class="mkwsPrev">&#60;&#60; ' + M('Prev') + '</span> | ';
            if (currentPage > 1)
                prev = '<a href="#" class="mkwsPrev" onclick="mkws.pagerPrev(\'' + teamName + '\');">'
-               +'&#60;&#60; ' + M('Prev') + '</a><b> | </b>';
+               +'&#60;&#60; ' + M('Prev') + '</a> | ';
 
            var middle = '';
            for(var i = firstClkbl; i <= lastClkbl; i++) {
                var numLabel = i;
                if(i == currentPage)
-                   numLabel = '<b>' + i + '</b>';
+                   numLabel = '<span class="mkwsSelected">' + i + '</span>';
 
                middle += '<a href="#" onclick="mkws.showPage(\'' + teamName + '\', ' + i + ')"> '
                    + numLabel + ' </a>';
            }
 
-           var next = '<b> | </b><span class="mkwsNext">' + M('Next') + ' &#62;&#62;</span>';
+           var next = ' | <span class="mkwsNext">' + M('Next') + ' &#62;&#62;</span>';
            if (pages - currentPage > 0)
-               next = '<b> | </b><a href="#" class="mkwsNext" onclick="mkws.pagerNext(\'' + teamName + '\')">'
+               next = ' | <a href="#" class="mkwsNext" onclick="mkws.pagerNext(\'' + teamName + '\')">'
                + M('Next') + ' &#62;&#62;</a>';
 
            var predots = '';
@@ -283,6 +117,13 @@ mkws.registerWidgetType('Pager', function() {
 });
 
 
+mkws.registerWidgetType('Results', function() {
+    // Nothing to do apart from act as an autosearch trigger
+    // Contained elements do all the real work
+    widget.autosearch(this);
+});
+
+
 mkws.registerWidgetType('Records', function() {
     var that = this;
     var team = this.team;
@@ -291,6 +132,7 @@ mkws.registerWidgetType('Records', function() {
        var html = [];
        for (var i = 0; i < data.hits.length; i++) {
            var hit = data.hits[i];
+            that.team.queue("record").publish(hit);
            var divId = team.recordElementId(hit.recid[0]);
            html.push('<div class="record mkwsTeam_' + team.name() + ' ' + divId + '">', renderSummary(hit), '</div>');
            // ### At some point, we may be able to move the
@@ -324,20 +166,18 @@ mkws.registerWidgetType('Navi', function() {
        var filters = that.team.filters();
        var text = "";
 
-       for (var i in filters) {
-           if (text) {
-               text += " | ";
-           }
-           var filter = filters[i];
-           if (filter.id) {
-               text += M('source') + ': <a class="crossout" href="#" onclick="mkws.delimitTarget(\'' + teamName +
-                   "', '" + filter.id + "'" + ');return false;">' + filter.name + '</a>';
-           } else {
-               text += M(filter.field) + ': <a class="crossout" href="#" onclick="mkws.delimitQuery(\'' + teamName +
-                   "', '" + filter.field + "', '" + filter.value + "'" +
-                   ');return false;">' + filter.value + '</a>';
-           }
-       }
+       filters.visitTargets(function(id, name) {
+           if (text) text += " | ";
+           text += M('source') + ': <a class="crossout" href="#" onclick="mkws.delimitTarget(\'' + teamName +
+               "', '" + id + "'" + ');return false;">' + name + '</a>';
+       });
+
+       filters.visitFields(function(field, value) {
+           if (text) text += " | ";
+           text += M(field) + ': <a class="crossout" href="#" onclick="mkws.delimitQuery(\'' + teamName +
+               "', '" + field + "', '" + value + "'" +
+               ');return false;">' + value + '</a>';
+       });
 
        $(that.node).html(text);
     });
@@ -353,7 +193,6 @@ mkws.registerWidgetType('Sort', function() {
     $(this.node).change(function() {
        that.team.set_sortOrder($(that.node).val());
        if (that.team.submitted()) {
-           that.team.resetPage();
            that.team.reShow();
        }
        return false;
@@ -367,9 +206,210 @@ mkws.registerWidgetType('Perpage', function() {
     $(this.node).change(function() {
        that.team.set_perpage($(that.node).val());
        if (that.team.submitted()) {
-           that.team.resetPage();
            that.team.reShow();
        }
        return false;
     });
 });
+
+
+mkws.registerWidgetType('Done', function() {
+    var that = this;
+
+    this.team.queue("complete").subscribe(function(n) {
+       $(that.node).html("Search complete: found " + n + " records");
+    });
+});
+
+
+mkws.registerWidgetType('Switch', function() {
+    var tname = this.team.name();
+    $(this.node).html('\
+<a href="#" onclick="mkws.switchView(\'' + tname + '\', \'records\')">Records</a><span> \
+| \
+</span><a href="#" onclick="mkws.switchView(\'' + tname + '\', \'targets\')">Targets</a>');
+});
+
+
+mkws.registerWidgetType('Search', function() {
+    var tname = this.team.name();
+    var M = mkws.M;
+
+    $(this.node).html('\
+<form name="mkwsSearchForm" class="mkwsSearchForm mkwsTeam_' + tname + '" action="" >\
+  <input class="mkwsQuery mkwsTeam_' + tname + '" type="text" size="' + this.config.query_width + '" />\
+  <input class="mkwsButton mkwsTeam_' + tname + '" type="submit" value="' + M('Search') + '" />\
+</form>');
+});
+
+
+mkws.registerWidgetType('SearchForm', function() {
+    var team = this.team;    
+    $(this.node).submit(function() {
+       var val = team.widget('Query').value();
+       team.newSearch(val);
+       return false;
+    });
+});
+
+
+mkws.registerWidgetType('Results', function() {
+    var tname = this.team.name();
+
+    $(this.node).html('\
+<table width="100%" border="0" cellpadding="6" cellspacing="0">\
+  <tr>\
+    <td class="mkwsTermlistContainer1 mkwsTeam_' + tname + '" width="250" valign="top">\
+      <div class="mkwsTermlists mkwsTeam_' + tname + '"></div>\
+    </td>\
+    <td class="mkwsMOTDContainer mkwsTeam_' + tname + '" valign="top">\
+      <div class="mkwsRanking mkwsTeam_' + tname + '"></div>\
+      <div class="mkwsPager mkwsTeam_' + tname + '"></div>\
+      <div class="mkwsNavi mkwsTeam_' + tname + '"></div>\
+      <div class="mkwsRecords mkwsTeam_' + tname + '"></div>\
+    </td>\
+  </tr>\
+  <tr>\
+    <td colspan="2">\
+      <div class="mkwsTermlistContainer2 mkwsTeam_' + tname + '"></div>\
+    </td>\
+  </tr>\
+</table>');
+});
+
+
+mkws.registerWidgetType('Ranking', function() {
+    var tname = this.team.name();
+    var that = this;
+    var M = mkws.M;
+
+    var s = '<form name="mkwsSelect" class="mkwsSelect mkwsTeam_' + tname + '" action="" >';
+    if (this.config.show_sort) {
+       s +=  M('Sort by') + ' ' + mkwsHtmlSort() + ' ';
+    }
+    if (this.config.show_perpage) {
+       s += M('and show') + ' ' + mkwsHtmlPerpage() + ' ' + M('per page') + '.';
+    }
+    s += '</form>';
+
+    $(this.node).html(s);
+
+
+    function mkwsHtmlSort() {
+        var order = that.team.sortOrder();
+
+       that.log("HTML sort, sortOrder = '" + order + "'");
+       var sort_html = '<select class="mkwsSort mkwsTeam_' + tname + '">';
+
+       for(var i = 0; i < that.config.sort_options.length; i++) {
+           var opt = that.config.sort_options[i];
+           var key = opt[0];
+           var val = opt.length == 1 ? opt[0] : opt[1];
+
+           sort_html += '<option value="' + key + '"';
+           if (order == key || order == val) {
+               sort_html += ' selected="selected"';
+           }
+           sort_html += '>' + M(val) + '</option>';
+       }
+       sort_html += '</select>';
+
+       return sort_html;
+    }
+
+    function mkwsHtmlPerpage() {
+        var perpage = that.team.perpage();
+
+       that.log("HTML perpage, perpage = " + perpage);
+       var perpage_html = '<select class="mkwsPerpage mkwsTeam_' + tname + '">';
+
+       for(var i = 0; i < that.config.perpage_options.length; i++) {
+           var key = that.config.perpage_options[i];
+
+           perpage_html += '<option value="' + key + '"';
+           if (key == perpage) {
+               perpage_html += ' selected="selected"';
+           }
+           perpage_html += '>' + key + '</option>';
+       }
+       perpage_html += '</select>';
+
+       return perpage_html;
+    }
+});
+
+
+mkws.registerWidgetType('Lang', function() {
+    // dynamic URL or static page? /path/foo?query=test
+    /* create locale language menu */
+    if (!this.config.show_lang) return;
+
+    var lang_default = "en";
+    var lang = this.config.lang || lang_default;
+    var list = [];
+
+    /* display a list of configured languages, or all */
+    var lang_options = this.config.lang_options || [];
+    var toBeIncluded = {};
+    for (var i = 0; i < lang_options.length; i++) {
+       toBeIncluded[lang_options[i]] = true;
+    }
+
+    for (var k in mkws.locale_lang) {
+       if (toBeIncluded[k] || lang_options.length == 0)
+           list.push(k);
+    }
+
+    // add english link
+    if (lang_options.length == 0 || toBeIncluded[lang_default])
+        list.push(lang_default);
+
+    this.log("Language menu for: " + list.join(", "));
+
+    /* the HTML part */
+    var data = "";
+    for (var i = 0; i < list.length; i++) {
+       var l = list[i];
+       if (data)
+           data += ' | ';
+
+       if (lang == l) {
+           data += ' <span>' + l + '</span> ';
+       } else {
+           data += ' <a href="' + lang_url(l) + '">' + l + '</a> '
+       }
+    }
+
+    $(this.node).html(data);
+
+
+    // set or re-set "lang" URL parameter
+    function lang_url(lang) {
+       var query = location.search;
+       // no query parameters? done
+       if (!query) {
+           return "?lang=" + lang;
+       }
+
+       // parameter does not exist
+       if (!query.match(/[\?&]lang=/)) {
+            return query + "&lang=" + lang;
+        }
+
+       // replace existing parameter
+       query = query.replace(/\?lang=([^&#;]*)/, "?lang=" + lang);
+       query = query.replace(/\&lang=([^&#;]*)/, "&lang=" + lang);
+       return query;
+    }
+});
+
+
+mkws.registerWidgetType('MOTD', function() {
+    var container = this.team.widget('MOTDContainer');
+    if (container) {
+       // Move the MOTD from the provided element down into the container
+       $(this.node).appendTo(container.node);
+    }
+});
+
+