summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/toastergui/static/js/table.js
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2015-05-08 17:24:11 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-05-14 18:04:09 +0100
commit7f8c44771cc6219ad7e58da7840fffbe93f11b39 (patch)
tree5c375661fec72a7d29c8cef6cc4a316d290ef5e1 /bitbake/lib/toaster/toastergui/static/js/table.js
parent2ac26e4294a029075071a8c6cfd5c2bdea801337 (diff)
downloadpoky-7f8c44771cc6219ad7e58da7840fffbe93f11b39.tar.gz
bitbake: toaster: Add toaster table widget
This widget provides a common client and backend widget to support presenting data tables in Toaster. It provides; data loading, paging, page size, ordering, filtering, column toggling, caching, column defaults, counts and search. (Bitbake rev: b3a6fa4861bf4495fbd39e2abb18b3a46c6eac18) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/static/js/table.js')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/table.js485
1 files changed, 485 insertions, 0 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/table.js b/bitbake/lib/toaster/toastergui/static/js/table.js
new file mode 100644
index 0000000000..8f5bb2ebe1
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/table.js
@@ -0,0 +1,485 @@
1'use strict';
2
3function tableInit(ctx){
4
5 if (ctx.url.length === 0) {
6 throw "No url supplied for retreiving data";
7 }
8
9 var tableChromeDone = false;
10 var tableTotal = 0;
11
12 var tableParams = {
13 limit : 25,
14 page : 1,
15 orderby : null,
16 filter : null,
17 search : null,
18 };
19
20 var defaultHiddenCols = [];
21
22 var table = $("#" + ctx.tableName);
23
24 /* if we're loading clean from a url use it's parameters as the default */
25 var urlParams = libtoaster.parseUrlParams();
26
27 /* Merge the tableParams and urlParams object properties */
28 tableParams = $.extend(tableParams, urlParams);
29
30 /* Now fix the types that .extend changed for us */
31 tableParams.limit = Number(tableParams.limit);
32 tableParams.page = Number(tableParams.page);
33
34 loadData(tableParams);
35
36 window.onpopstate = function(event){
37 if (event.state){
38 tableParams = event.state.tableParams;
39 /* We skip loadData and just update the table */
40 updateTable(event.state.tableData);
41 }
42 };
43
44 function loadData(tableParams){
45 $.ajax({
46 type: "GET",
47 url: ctx.url,
48 data: tableParams,
49 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
50 success: function(tableData) {
51 updateTable(tableData);
52 window.history.pushState({
53 tableData: tableData,
54 tableParams: tableParams
55 }, null, libtoaster.dumpsUrlParams(tableParams));
56 },
57
58 error: function (_data) {
59 console.warn("Call failed");
60 console.warn(_data);
61 }
62 });
63 }
64
65 function updateTable(tableData) {
66 var tableBody = table.children("tbody");
67 var paginationBtns = $('#pagination-'+ctx.tableName);
68
69 /* To avoid page re-layout flicker when paging set fixed height */
70 table.css("visibility", "hidden");
71 table.css("padding-bottom", table.height());
72
73 /* Reset table components */
74 tableBody.html("");
75 paginationBtns.html("");
76
77 if (tableParams.search)
78 $('.remove-search-btn-'+ctx.tableName).show();
79 else
80 $('.remove-search-btn-'+ctx.tableName).hide();
81
82 $('.table-count-' + ctx.tableName).text(tableData.total);
83 tableTotal = tableData.total;
84
85 if (tableData.total === 0){
86 $("#table-container-"+ctx.tableName).hide();
87 $("#new-search-input-"+ctx.tableName).val(tableParams.search);
88 $("#no-results-"+ctx.tableName).show();
89 return;
90 } else {
91 $("#table-container-"+ctx.tableName).show();
92 $("#no-results-"+ctx.tableName).hide();
93 }
94
95
96 setupTableChrome(tableData);
97
98
99 /* Add table data rows */
100 for (var i in tableData.rows){
101 var row = $("<tr></tr>");
102 for (var key_j in tableData.rows[i]){
103 var td = $("<td></td>");
104 td.prop("class", key_j);
105 if (tableData.rows[i][key_j]){
106 td.html(tableData.rows[i][key_j]);
107 }
108 row.append(td);
109 }
110 tableBody.append(row);
111
112 /* If we have layerbtns then initialise them */
113 layerBtnsInit(ctx);
114
115 /* If we have popovers initialise them now */
116 $('td > a.btn').popover({
117 html:true,
118 placement:'left',
119 container:'body',
120 trigger:'manual'
121 }).click(function(e){
122 $('td > a.btn').not(this).popover('hide');
123 /* ideally we would use 'toggle' here
124 * but it seems buggy in our Bootstrap version
125 */
126 $(this).popover('show');
127 e.stopPropagation();
128 });
129
130 /* enable help information tooltip */
131 $(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
132 }
133
134 /* Setup the pagination controls */
135
136 var start = tableParams.page - 2;
137 var end = tableParams.page + 2;
138 var numPages = Math.ceil(tableData.total/tableParams.limit);
139
140 if (tableParams.page < 3)
141 end = 5;
142
143 for (var page_i=1; page_i <= numPages; page_i++){
144 if (page_i >= start && page_i <= end){
145 var btn = $('<li><a href="#" class="page">'+page_i+'</a></li>');
146
147 if (page_i === tableParams.page){
148 btn.addClass("active");
149 }
150
151 /* Add the click handler */
152 btn.click(pageButtonClicked);
153 paginationBtns.append(btn);
154 }
155 }
156 table.css("padding-bottom", 0);
157 loadColumnsPreference();
158
159 $("table").css("visibility", "visible");
160 }
161
162 function setupTableChrome(tableData){
163 if (tableChromeDone === true)
164 return;
165
166 var tableHeadRow = table.find("thead tr");
167 var editColMenu = $("#table-chrome-"+ctx.tableName).find(".editcol");
168
169 tableHeadRow.html("");
170 editColMenu.html("");
171
172 if (!tableParams.orderby && tableData.default_orderby){
173 tableParams.orderby = tableData.default_orderby;
174 }
175
176 /* Add table header and column toggle menu */
177 for (var i in tableData.columns){
178 var col = tableData.columns[i];
179 var header = $("<th></th>");
180 header.prop("class", col.field_name);
181
182 /* Setup the help text */
183 if (col.help_text.length > 0) {
184 var help_text = $('<i class="icon-question-sign get-help"> </i>');
185 help_text.tooltip({title: col.help_text});
186 header.append(help_text);
187 }
188
189 /* Setup the orderable title */
190 if (col.orderable) {
191 var title = $('<a href=\"#\" ></a>');
192
193 title.data('field-name', col.field_name);
194 title.text(col.title);
195 title.click(sortColumnClicked);
196
197 header.append(title);
198
199 header.append(' <i class="icon-caret-down" style="display:none"></i>');
200 header.append(' <i class="icon-caret-up" style="display:none"></i>');
201
202 /* If we're currently ordered setup the visual indicator */
203 if (col.field_name === tableParams.orderby ||
204 '-' + col.field_name === tableParams.orderby){
205 header.children("a").addClass("sorted");
206
207 if (tableParams.orderby.indexOf("-") === -1){
208 header.find('.icon-caret-down').show();
209 } else {
210 header.find('.icon-caret-up').show();
211 }
212 }
213
214 } else {
215 /* Not orderable */
216 header.addClass("muted");
217 header.css("font-weight", "normal");
218 header.append(col.title+' ');
219 }
220
221 /* Setup the filter button */
222 if (col.filter_name){
223 var filterBtn = $('<a href="#" role="button" class="pull-right btn btn-mini" data-toggle="modal"><i class="icon-filter filtered"></i></a>');
224
225 filterBtn.data('filter-name', col.filter_name);
226 filterBtn.click(filterOpenClicked);
227
228 /* If we're currently being filtered setup the visial indicator */
229 if (tableParams.filter &&
230 tableParams.filter.match('^'+col.filter_name)) {
231
232 filterBtn.addClass("btn-primary");
233
234 filterBtn.tooltip({
235 html: true,
236 title: '<button class="btn btn-small btn-primary" onClick=\'$("#clear-filter-btn").click();\'>Clear filter</button>',
237 placement: 'bottom',
238 delay: {
239 hide: 1500,
240 show: 400,
241 },
242 });
243 }
244 header.append(filterBtn);
245 }
246
247 /* Done making the header now add it */
248 tableHeadRow.append(header);
249
250 /* Now setup the checkbox state and click handler */
251 var toggler = $('<li><label class="checkbox">'+col.title+'<input type="checkbox" id="checkbox-'+ col.field_name +'" class="col-toggle" value="'+col.field_name+'" /></label></li>');
252
253 var togglerInput = toggler.find("input");
254
255 togglerInput.attr("checked","checked");
256
257 /* If we can hide the column enable the checkbox action */
258 if (col.hideable){
259 togglerInput.click(colToggleClicked);
260 } else {
261 toggler.find("label").addClass("muted");
262 togglerInput.attr("disabled", "disabled");
263 }
264
265 if (col.hidden) {
266 defaultHiddenCols.push(col.field_name);
267 }
268
269 editColMenu.append(toggler);
270 } /* End for each column */
271
272 tableChromeDone = true;
273 }
274
275 /* Display or hide table columns based on the cookie preference or defaults */
276 function loadColumnsPreference(){
277 var cookie_data = $.cookie("cols");
278
279 if (cookie_data) {
280 var cols_hidden = JSON.parse($.cookie("cols"));
281
282 /* For each of the columns check if we should hide them
283 * also update the checked status in the Edit columns menu
284 */
285 $("#"+ctx.tableName+" th").each(function(){
286 for (var i in cols_hidden){
287 if ($(this).hasClass(cols_hidden[i])){
288 $("."+cols_hidden[i]).hide();
289 $("#checkbox-"+cols_hidden[i]).removeAttr("checked");
290 }
291 }
292 });
293 } else {
294 /* Disable these columns by default when we have no columns
295 * user setting.
296 */
297 for (var i in defaultHiddenCols) {
298 $("."+defaultHiddenCols[i]).hide();
299 $("#checkbox-"+defaultHiddenCols[i]).removeAttr("checked");
300 }
301 }
302 }
303
304 function sortColumnClicked(){
305
306 /* We only have one sort at a time so remove any existing sort indicators */
307 $("#"+ctx.tableName+" th .icon-caret-down").hide();
308 $("#"+ctx.tableName+" th .icon-caret-up").hide();
309 $("#"+ctx.tableName+" th a").removeClass("sorted");
310
311 var fieldName = $(this).data('field-name');
312
313 /* if we're already sorted sort the other way */
314 if (tableParams.orderby === fieldName &&
315 tableParams.orderby.indexOf('-') === -1) {
316 tableParams.orderby = '-' + $(this).data('field-name');
317 $(this).parent().children('.icon-caret-up').show();
318 } else {
319 tableParams.orderby = $(this).data('field-name');
320 $(this).parent().children('.icon-caret-down').show();
321 }
322
323 $(this).addClass("sorted");
324
325 loadData(tableParams);
326 }
327
328 function pageButtonClicked(e) {
329 tableParams.page = Number($(this).text());
330 loadData(tableParams);
331 /* Stop page jumps when clicking on # links */
332 e.preventDefault();
333 }
334
335 /* Toggle a table column */
336 function colToggleClicked (){
337 var col = $(this).val();
338 var disabled_cols = [];
339
340 if ($(this).prop("checked")) {
341 $("."+col).show();
342 } else {
343 $("."+col).hide();
344 /* If we're ordered by the column we're hiding remove the order by */
345 if (col === tableParams.orderby ||
346 '-' + col === tableParams.orderby){
347 tableParams.orderby = null;
348 loadData(tableParams);
349 }
350 }
351
352 /* Update the cookie with the unchecked columns */
353 $(".col-toggle").not(":checked").map(function(){
354 disabled_cols.push($(this).val());
355 });
356
357 $.cookie("cols", JSON.stringify(disabled_cols));
358 }
359
360 function filterOpenClicked(){
361 var filterName = $(this).data('filter-name');
362
363 /* We need to pass in the curren search so that the filter counts take
364 * into account the current search filter
365 */
366 var params = {
367 'name' : filterName,
368 'search': tableParams.search
369 };
370
371 $.ajax({
372 type: "GET",
373 url: ctx.url + 'filterinfo',
374 data: params,
375 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
376 success: function (filterData) {
377 var filterActionRadios = $('#filter-actions');
378
379 $('#filter-modal-title').text(filterData.title);
380
381 filterActionRadios.text("");
382
383 for (var i in filterData.filter_actions){
384 var filterAction = filterData.filter_actions[i];
385
386 var action = $('<label class="radio"><input type="radio" name="filter" value=""><span class="filter-title"></span></label>');
387 var actionTitle = filterAction.title + ' (' + filterAction.count + ')';
388
389 var radioInput = action.children("input");
390
391 action.children(".filter-title").text(actionTitle);
392
393 radioInput.val(filterName + ':' + filterAction.name);
394
395 /* Setup the current selected filter, default to 'all' if
396 * no current filter selected.
397 */
398 if ((tableParams.filter &&
399 tableParams.filter === radioInput.val()) ||
400 filterAction.name == 'all') {
401 radioInput.attr("checked", "checked");
402 }
403
404 filterActionRadios.append(action);
405 }
406
407 $('#filter-modal').modal('show');
408 }
409 });
410 }
411
412
413 $(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
414
415 /* Keep the Edit columns menu open after click by eating the event */
416 $('.dropdown-menu').click(function(e) {
417 e.stopPropagation();
418 });
419
420 $(".pagesize").val(tableParams.limit);
421
422 /* page size selector */
423 $(".pagesize").change(function(){
424 tableParams.limit = Number(this.value);
425 if ((tableParams.page * tableParams.limit) > tableTotal)
426 tableParams.page = 1;
427
428 loadData(tableParams);
429 /* sync the other selectors on the page */
430 $(".pagesize").val(this.value);
431 });
432
433 $("#search-submit-"+ctx.tableName).click(function(e){
434 var searchTerm = $("#search-input-"+ctx.tableName).val();
435
436 tableParams.page = 1;
437 tableParams.search = searchTerm;
438 tableParams.filter = null;
439
440 loadData(tableParams);
441
442 e.preventDefault();
443 });
444
445 $('.remove-search-btn-'+ctx.tableName).click(function(e){
446 e.preventDefault();
447
448 tableParams.page = 1;
449 tableParams.search = null;
450 loadData(tableParams);
451
452 $("#search-input-"+ctx.tableName).val("");
453 $(this).hide();
454 });
455
456 $("#search-input-"+ctx.tableName).keyup(function(e){
457 if (e.which === 13)
458 $('#search-submit-'+ctx.tableName).click();
459 });
460
461 /* Stop page jumps when clicking on # links */
462 $('a[href="#"]').click(function(e){
463 e.preventDefault();
464 });
465
466 $("#clear-filter-btn").click(function(){
467 tableParams.filter = null;
468 loadData(tableParams);
469 });
470
471 $("#filter-modal-form").submit(function(e){
472 e.preventDefault();
473
474 tableParams.filter = $(this).find("input[type='radio']:checked").val();
475
476 /* All === remove filter */
477 if (tableParams.filter.match(":all$"))
478 tableParams.filter = null;
479
480 loadData(tableParams);
481
482
483 $('#filter-modal').modal('hide');
484 });
485}