Add attribute-parsing code to widget constructor.
[mkws-moved-to-github.git] / src / mkws-widgets.js
1 // Factory function for widget objects.
2 function widget($, team, type, node) {
3     // Static register of attributes that do not contribute to config
4     var ignoreAttrs = {
5         id:1, class:1, style:1, name:1, action:1, type:1, size:1,
6         value:1, width:1, valign:1
7     };
8
9     var that = {
10         team: team,
11         type: type,
12         node: node,
13         config: Object.create(team.config())
14     };
15
16     function log(s) {
17         team.log(s);
18     }
19     that.log = log;
20
21     that.toString = function() {
22         return '[Widget ' + team.name() + ':' + type + ']';
23     };
24
25     for (var i = 0; i < node.attributes.length; i++) {
26         var a = node.attributes[i];
27         if (a.name === 'data-mkws-config') {
28             // Treat as a JSON fragment configuring just this widget
29             log(node + ": parsing config fragment '" + a.value + "'");
30             var data = $.parseJSON(a.value);
31             for (var key in data) {
32                 log(node + ": adding config element " + key + "='" + data[key] + "'");
33                 that.config[key] = data[key];
34             }
35         } else if (a.name.match (/^data-mkws-/)) {
36             var name = a.name.replace(/^data-mkws-/, '')
37             that.config[name] = a.value;
38             log(node + ": set data-mkws attribute " + name + "='" + a.value + "'");
39         } else if (!ignoreAttrs[a.name]) {
40             that.config[a.name] = a.value;
41             log(node + ": set regular attribute " + a.name + "='" + a.value + "'");
42         }
43     }
44
45     var fn = mkws.promotionFunction(type);
46     if (fn) {
47         fn.call(that);
48         log("made " + type + " widget(node=" + node + ")");
49     } else {
50         log("made UNPROMOTED widget(type=" + type + ", node=" + node + ")");
51     }
52
53     return that;
54 }
55
56
57 // Functions follow for promoting the regular widget object into
58 // widgets of specific types. These could be moved into their own
59 // source files.
60
61
62 mkws.registerWidgetType('Targets', function() {
63     var that = this;
64     var M = mkws.M;
65
66     this.team.queue("targets").subscribe(function(data) {
67         var table ='<table><thead><tr>' +
68             '<td>' + M('Target ID') + '</td>' +
69             '<td>' + M('Hits') + '</td>' +
70             '<td>' + M('Diags') + '</td>' +
71             '<td>' + M('Records') + '</td>' +
72             '<td>' + M('State') + '</td>' +
73             '</tr></thead><tbody>';
74
75         for (var i = 0; i < data.length; i++) {
76             table += "<tr><td>" + data[i].id +
77                 "</td><td>" + data[i].hits +
78                 "</td><td>" + data[i].diagnostic +
79                 "</td><td>" + data[i].records +
80                 "</td><td>" + data[i].state + "</td></tr>";
81         }
82
83         table += '</tbody></table>';
84         var subnode = $(that.node).children('.mkwsBytarget');
85         subnode.html(table);
86     });
87 });
88
89
90 mkws.registerWidgetType('Stat', function() {
91     var that = this;
92     var M = mkws.M;
93
94     this.team.queue("stat").subscribe(function(data) {
95         if (that.node.length === 0)  alert("huh?!");
96
97         $(that.node).html('<span class="head">' + M('Status info') + '</span>' +
98             ' -- ' +
99             '<span class="clients">' + M('Active clients') + ': ' + data.activeclients + '/' + data.clients + '</span>' +
100             ' -- ' +
101             '<span class="records">' + M('Retrieved records') + ': ' + data.records + '/' + data.hits + '</span>');
102     });
103 });
104
105
106 mkws.registerWidgetType('Termlists', function() {
107     var that = this;
108     var M = mkws.M;
109
110     this.team.queue("termlists").subscribe(function(data) {
111         if (!that.node) {
112             alert("termlists event when there are no termlists");
113             return;
114         }
115
116         // no facets: this should never happen
117         var facets = that.config.facets;
118         if (!facets || facets.length == 0) {
119             alert("onTerm called even though we have no facets: " + $.toJSON(data));
120             $(that.node).hide();
121             return;
122         }
123
124         // display if we first got results
125         $(that.node).show();
126
127         var acc = [];
128         acc.push('<div class="title">' + M('Termlists') + '</div>');
129
130         for (var i = 0; i < facets.length; i++) {
131             if (facets[i] == "xtargets") {
132                 addSingleFacet(acc, "Sources",  data.xtargets, 16, null);
133             } else if (facets[i] == "subject") {
134                 addSingleFacet(acc, "Subjects", data.subject,  10, "subject");
135             } else if (facets[i] == "author") {
136                 addSingleFacet(acc, "Authors",  data.author,   10, "author");
137             } else {
138                 alert("bad facet configuration: '" + facets[i] + "'");
139             }
140         }
141
142         $(that.node).html(acc.join(''));
143
144         function addSingleFacet(acc, caption, data, max, pzIndex) {
145             var teamName = that.team.name();
146             acc.push('<div class="facet mkwsFacet' + caption + ' mkwsTeam_' + teamName + '">');
147             acc.push('<div class="termtitle">' + M(caption) + '</div>');
148             for (var i = 0; i < data.length && i < max; i++) {
149                 acc.push('<div class="term">');
150                 acc.push('<a href="#" ');
151                 var action = '';
152                 if (!pzIndex) {
153                     // Special case: target selection
154                     acc.push('target_id='+data[i].id+' ');
155                     if (!that.team.targetFiltered(data[i].id)) {
156                         action = 'mkws.limitTarget(\'' + teamName + '\', this.getAttribute(\'target_id\'),this.firstChild.nodeValue)';
157                     }
158                 } else {
159                     action = 'mkws.limitQuery(\'' + teamName + '\', \'' + pzIndex + '\', this.firstChild.nodeValue)';
160                 }
161                 acc.push('onclick="' + action + ';return false;">' + data[i].name + '</a>'
162                          + ' <span>' + data[i].freq + '</span>');
163                 acc.push('</div>');
164             }
165             acc.push('</div>');
166         }
167     });
168 });
169
170
171 mkws.registerWidgetType('Pager', function() {
172     var that = this;
173     var M = mkws.M;
174
175     this.team.queue("pager").subscribe(function(data) {
176         $(that.node).html(drawPager(data))
177
178         function drawPager(data) {
179             var teamName = that.team.name();
180             var s = '<div style="float: right">' + M('Displaying') + ': '
181                 + (data.start + 1) + ' ' + M('to') + ' ' + (data.start + data.num) +
182                 ' ' + M('of') + ' ' + data.merged + ' (' + M('found') + ': '
183                 + data.total + ')</div>';
184
185             //client indexes pages from 1 but pz2 from 0
186             var onsides = 6;
187             var pages = Math.ceil(that.team.totalRecordCount() / that.team.perpage());
188             var currentPage = that.team.currentPage();
189
190             var firstClkbl = (currentPage - onsides > 0)
191                 ? currentPage - onsides
192                 : 1;
193
194             var lastClkbl = firstClkbl + 2*onsides < pages
195                 ? firstClkbl + 2*onsides
196                 : pages;
197
198             var prev = '<span class="mkwsPrev">&#60;&#60; ' + M('Prev') + '</span><b> | </b>';
199             if (currentPage > 1)
200                 prev = '<a href="#" class="mkwsPrev" onclick="mkws.pagerPrev(\'' + teamName + '\');">'
201                 +'&#60;&#60; ' + M('Prev') + '</a><b> | </b>';
202
203             var middle = '';
204             for(var i = firstClkbl; i <= lastClkbl; i++) {
205                 var numLabel = i;
206                 if(i == currentPage)
207                     numLabel = '<b>' + i + '</b>';
208
209                 middle += '<a href="#" onclick="mkws.showPage(\'' + teamName + '\', ' + i + ')"> '
210                     + numLabel + ' </a>';
211             }
212
213             var next = '<b> | </b><span class="mkwsNext">' + M('Next') + ' &#62;&#62;</span>';
214             if (pages - currentPage > 0)
215                 next = '<b> | </b><a href="#" class="mkwsNext" onclick="mkws.pagerNext(\'' + teamName + '\')">'
216                 + M('Next') + ' &#62;&#62;</a>';
217
218             var predots = '';
219             if (firstClkbl > 1)
220                 predots = '...';
221
222             var postdots = '';
223             if (lastClkbl < pages)
224                 postdots = '...';
225
226             s += '<div style="float: clear">'
227                 + prev + predots + middle + postdots + next + '</div>';
228
229             return s;
230         }
231     });
232 });
233
234
235 mkws.registerWidgetType('Records', function() {
236     var that = this;
237     var team = this.team;
238
239     this.team.queue("records").subscribe(function(data) {
240         var html = [];
241         for (var i = 0; i < data.hits.length; i++) {
242             var hit = data.hits[i];
243             var divId = team.recordElementId(hit.recid[0]);
244             html.push('<div class="record mkwsTeam_' + team.name() + ' ' + divId + '">', renderSummary(hit), '</div>');
245             // ### At some point, we may be able to move the
246             // m_currentRecordId and m_currentRecordData members
247             // from the team object into this widget.
248             if (hit.recid == team.currentRecordId()) {
249                 if (team.currentRecordData())
250                     html.push(team.renderDetails(team.currentRecordData()));
251             }
252         }
253         $(that.node).html(html.join(''));
254
255         function renderSummary(hit)
256         {
257             var template = team.loadTemplate("Summary");
258             hit._id = team.recordElementId(hit.recid[0]);
259             hit._onclick = "mkws.showDetails('" + team.name() + "', '" + hit.recid[0] + "');return false;"
260             return template(hit);
261         }
262     });
263 });
264
265
266 mkws.registerWidgetType('Navi', function() {
267     var that = this;
268     var teamName = this.team.name();
269     var M = mkws.M;
270
271     this.team.queue("navi").subscribe(function() {
272         var filters = that.team.filters();
273         var text = "";
274
275         for (var i in filters) {
276             if (text) {
277                 text += " | ";
278             }
279             var filter = filters[i];
280             if (filter.id) {
281                 text += M('source') + ': <a class="crossout" href="#" onclick="mkws.delimitTarget(\'' + teamName +
282                     "', '" + filter.id + "'" + ');return false;">' + filter.name + '</a>';
283             } else {
284                 text += M(filter.field) + ': <a class="crossout" href="#" onclick="mkws.delimitQuery(\'' + teamName +
285                     "', '" + filter.field + "', '" + filter.value + "'" +
286                     ');return false;">' + filter.value + '</a>';
287             }
288         }
289
290         $(that.node).html(text);
291     });
292 });
293
294
295 // It seems this and the Perpage widget doen't need to subscribe to
296 // anything, since they produce events rather than consuming them.
297 //
298 mkws.registerWidgetType('Sort', function() {
299     var that = this;
300
301     $(this.node).change(function() {
302         that.team.set_sortOrder($(that.node).val());
303         if (that.team.submitted()) {
304             that.team.resetPage();
305             that.team.reShow();
306         }
307         return false;
308     });
309 });
310
311
312 mkws.registerWidgetType('Perpage', function() {
313     var that = this;
314
315     $(this.node).change(function() {
316         that.team.set_perpage($(that.node).val());
317         if (that.team.submitted()) {
318             that.team.resetPage();
319             that.team.reShow();
320         }
321         return false;
322     });
323 });