xref: /OK3568_Linux_fs/yocto/bitbake/lib/toaster/toastergui/static/js/table.js (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun'use strict';
2*4882a593Smuzhiyun
3*4882a593Smuzhiyunfunction tableInit(ctx){
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun  if (ctx.url.length === 0) {
6*4882a593Smuzhiyun    throw "No url supplied for retreiving data";
7*4882a593Smuzhiyun  }
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun  var tableChromeDone = false;
10*4882a593Smuzhiyun  var tableTotal = 0;
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun  var tableParams = {
13*4882a593Smuzhiyun    limit : 25,
14*4882a593Smuzhiyun    page : 1,
15*4882a593Smuzhiyun    orderby : null,
16*4882a593Smuzhiyun    filter : null,
17*4882a593Smuzhiyun    search : null,
18*4882a593Smuzhiyun    default_orderby: null,
19*4882a593Smuzhiyun  };
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun  var defaultHiddenCols = [];
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun  var table =  $("#" + ctx.tableName);
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun  /* if we're loading clean from a url use it's parameters as the default */
26*4882a593Smuzhiyun  var urlParams = libtoaster.parseUrlParams();
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun  /* Merge the tableParams and urlParams object properties  */
29*4882a593Smuzhiyun  tableParams = $.extend(tableParams, urlParams);
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun  /* Now fix the types that .extend changed for us */
32*4882a593Smuzhiyun  tableParams.limit = Number(tableParams.limit);
33*4882a593Smuzhiyun  tableParams.page = Number(tableParams.page);
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun  loadData(tableParams);
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun  // clicking on this set of elements removes the search
38*4882a593Smuzhiyun  var clearSearchElements = $('.remove-search-btn-'+ctx.tableName +
39*4882a593Smuzhiyun                              ', .show-all-'+ctx.tableName);
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun  function loadData(tableParams){
42*4882a593Smuzhiyun    table.trigger("table-loading");
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun    $.ajax({
45*4882a593Smuzhiyun        type: "GET",
46*4882a593Smuzhiyun        url: ctx.url,
47*4882a593Smuzhiyun        data: tableParams,
48*4882a593Smuzhiyun        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
49*4882a593Smuzhiyun        success: function(tableData) {
50*4882a593Smuzhiyun          updateTable(tableData);
51*4882a593Smuzhiyun          window.history.replaceState(null, null,
52*4882a593Smuzhiyun            libtoaster.dumpsUrlParams(tableParams));
53*4882a593Smuzhiyun        }
54*4882a593Smuzhiyun    });
55*4882a593Smuzhiyun  }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun  function updateTable(tableData) {
58*4882a593Smuzhiyun    var tableBody = table.children("tbody");
59*4882a593Smuzhiyun    var pagination = $('#pagination-'+ctx.tableName);
60*4882a593Smuzhiyun    var paginationBtns = pagination.children('ul');
61*4882a593Smuzhiyun    var tableContainer = $("#table-container-"+ctx.tableName);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun    tableContainer.css("visibility", "hidden");
64*4882a593Smuzhiyun    /* To avoid page re-layout flicker when paging set fixed height */
65*4882a593Smuzhiyun    table.css("padding-bottom", table.height());
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun    /* Reset table components */
68*4882a593Smuzhiyun    tableBody.html("");
69*4882a593Smuzhiyun    paginationBtns.html("");
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun    if (tableParams.search)
72*4882a593Smuzhiyun      clearSearchElements.show();
73*4882a593Smuzhiyun    else
74*4882a593Smuzhiyun      clearSearchElements.hide();
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun    $('.table-count-' + ctx.tableName).text(tableData.total);
77*4882a593Smuzhiyun    tableTotal = tableData.total;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun    if (tableData.total === 0){
80*4882a593Smuzhiyun      tableContainer.hide();
81*4882a593Smuzhiyun      /* No results caused by a search returning nothing */
82*4882a593Smuzhiyun      if (tableParams.search) {
83*4882a593Smuzhiyun        if ($("#no-results-special-"+ctx.tableName).length > 0) {
84*4882a593Smuzhiyun          /* use this page's special no-results form instead of the default */
85*4882a593Smuzhiyun          $("#no-results-search-input-"+ctx.tableName).val(tableParams.search);
86*4882a593Smuzhiyun          $("#no-results-special-"+ctx.tableName).show();
87*4882a593Smuzhiyun          $("#results-found-"+ctx.tableName).hide();
88*4882a593Smuzhiyun        } else {
89*4882a593Smuzhiyun          $("#new-search-input-"+ctx.tableName).val(tableParams.search);
90*4882a593Smuzhiyun          $("#no-results-"+ctx.tableName).show();
91*4882a593Smuzhiyun        }
92*4882a593Smuzhiyun      }
93*4882a593Smuzhiyun      else {
94*4882a593Smuzhiyun        /* No results caused by there being no data */
95*4882a593Smuzhiyun        $("#empty-state-"+ctx.tableName).show();
96*4882a593Smuzhiyun      }
97*4882a593Smuzhiyun      table.trigger("table-done", [tableData.total, tableParams]);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun      return;
100*4882a593Smuzhiyun    } else {
101*4882a593Smuzhiyun      tableContainer.show();
102*4882a593Smuzhiyun      $("#no-results-"+ctx.tableName).hide();
103*4882a593Smuzhiyun      $("#empty-state-"+ctx.tableName).hide();
104*4882a593Smuzhiyun    }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun    setupTableChrome(tableData);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun    /* Add table data rows */
109*4882a593Smuzhiyun    var column_index;
110*4882a593Smuzhiyun    for (var i in tableData.rows){
111*4882a593Smuzhiyun      /* only display if the column is display-able */
112*4882a593Smuzhiyun      var row = $("<tr></tr>");
113*4882a593Smuzhiyun      column_index = -1;
114*4882a593Smuzhiyun      for (var key_j in tableData.rows[i]){
115*4882a593Smuzhiyun        var td = $("<td></td>");
116*4882a593Smuzhiyun        td.prop("class", key_j);
117*4882a593Smuzhiyun        if (tableData.rows[i][key_j]){
118*4882a593Smuzhiyun          td.html(tableData.rows[i][key_j]);
119*4882a593Smuzhiyun        }
120*4882a593Smuzhiyun        row.append(td);
121*4882a593Smuzhiyun      }
122*4882a593Smuzhiyun      tableBody.append(row);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun      /* If we have layerbtns then initialise them */
125*4882a593Smuzhiyun      layerBtnsInit();
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun      /* If we have popovers initialise them now */
128*4882a593Smuzhiyun      $('td > a.btn').popover({
129*4882a593Smuzhiyun        html:true,
130*4882a593Smuzhiyun        placement:'left',
131*4882a593Smuzhiyun        container:'body',
132*4882a593Smuzhiyun        trigger:'manual'
133*4882a593Smuzhiyun      }).click(function(e){
134*4882a593Smuzhiyun        $('td > a.btn').not(this).popover('hide');
135*4882a593Smuzhiyun        /* ideally we would use 'toggle' here
136*4882a593Smuzhiyun         * but it seems buggy in our Bootstrap version
137*4882a593Smuzhiyun         */
138*4882a593Smuzhiyun        $(this).popover('show');
139*4882a593Smuzhiyun        e.stopPropagation();
140*4882a593Smuzhiyun      });
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun      /* enable help information tooltip */
143*4882a593Smuzhiyun      $(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
144*4882a593Smuzhiyun    }
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun    /* Setup the pagination controls */
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun    var start = tableParams.page - 2;
149*4882a593Smuzhiyun    var end = tableParams.page + 2;
150*4882a593Smuzhiyun    var numPages = Math.ceil(tableData.total/tableParams.limit);
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun    if (numPages >  1){
153*4882a593Smuzhiyun      if (tableParams.page < 3)
154*4882a593Smuzhiyun        end = 5;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun      for (var page_i=1; page_i <= numPages;  page_i++){
157*4882a593Smuzhiyun        if (page_i >= start && page_i <= end){
158*4882a593Smuzhiyun          var btn = $('<li><a href="#" class="page">'+page_i+'</a></li>');
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun          if (page_i === tableParams.page){
161*4882a593Smuzhiyun            btn.addClass("active");
162*4882a593Smuzhiyun          }
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun          /* Add the click handler */
165*4882a593Smuzhiyun          btn.click(pageButtonClicked);
166*4882a593Smuzhiyun          paginationBtns.append(btn);
167*4882a593Smuzhiyun        }
168*4882a593Smuzhiyun      }
169*4882a593Smuzhiyun    }
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun    loadColumnsPreference();
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun    table.css("padding-bottom", 0);
174*4882a593Smuzhiyun    tableContainer.css("visibility", "visible");
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun    /* If we have a hash in the url try and highlight that item in the table */
177*4882a593Smuzhiyun    if (window.location.hash){
178*4882a593Smuzhiyun      var highlight = $("table a[name="+window.location.hash.replace('#',''));
179*4882a593Smuzhiyun      if (highlight.length > 0){
180*4882a593Smuzhiyun        highlight.parents("tr").addClass('highlight');
181*4882a593Smuzhiyun        window.scroll(0, highlight.position().top - 50);
182*4882a593Smuzhiyun      }
183*4882a593Smuzhiyun    }
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun    table.trigger("table-done", [tableData.total, tableParams]);
186*4882a593Smuzhiyun  }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun  function setupTableChrome(tableData){
189*4882a593Smuzhiyun    if (tableChromeDone === true)
190*4882a593Smuzhiyun      return;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun    var tableHeadRow = table.find("thead > tr");
193*4882a593Smuzhiyun    var editColMenu = $("#table-chrome-"+ctx.tableName).find(".editcol");
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun    tableHeadRow.html("");
196*4882a593Smuzhiyun    editColMenu.html("");
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun    tableParams.default_orderby = tableData.default_orderby;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun    if (!tableParams.orderby && tableData.default_orderby){
201*4882a593Smuzhiyun      tableParams.orderby = tableData.default_orderby;
202*4882a593Smuzhiyun    }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun    /* Add table header and column toggle menu */
205*4882a593Smuzhiyun    var column_edit_entries = [];
206*4882a593Smuzhiyun    for (var i in tableData.columns){
207*4882a593Smuzhiyun      var col = tableData.columns[i];
208*4882a593Smuzhiyun      if (col.displayable === false) {
209*4882a593Smuzhiyun        continue;
210*4882a593Smuzhiyun      }
211*4882a593Smuzhiyun      var header = $("<th></th>");
212*4882a593Smuzhiyun      header.prop("class", col.field_name);
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun      /* Setup the help text */
215*4882a593Smuzhiyun      if (col.help_text.length > 0) {
216*4882a593Smuzhiyun        var help_text = $('<span class="glyphicon glyphicon-question-sign get-help"> </span>');
217*4882a593Smuzhiyun        help_text.tooltip({title: col.help_text});
218*4882a593Smuzhiyun        header.append(help_text);
219*4882a593Smuzhiyun      }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun      /* Setup the orderable title */
222*4882a593Smuzhiyun      if (col.orderable) {
223*4882a593Smuzhiyun        var title = $('<a href=\"#\" ></a>');
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun        title.data('field-name', col.field_name);
226*4882a593Smuzhiyun        title.attr('data-sort-field', col.field_name);
227*4882a593Smuzhiyun        title.text(col.title);
228*4882a593Smuzhiyun        title.click(sortColumnClicked);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun        header.append(title);
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun        header.append(' <i class="icon-caret-down" style="display:none"></i>');
233*4882a593Smuzhiyun        header.append(' <i class="icon-caret-up" style="display:none"></i>');
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun        /* If we're currently ordered setup the visual indicator */
236*4882a593Smuzhiyun        if (col.field_name === tableParams.orderby ||
237*4882a593Smuzhiyun          '-' + col.field_name === tableParams.orderby){
238*4882a593Smuzhiyun          header.children("a").addClass("sorted");
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun          if (tableParams.orderby.indexOf("-") === -1){
241*4882a593Smuzhiyun            header.find('.icon-caret-down').show();
242*4882a593Smuzhiyun          } else {
243*4882a593Smuzhiyun            header.find('.icon-caret-up').show();
244*4882a593Smuzhiyun          }
245*4882a593Smuzhiyun        }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun       if (col.field_name === tableData.default_orderby){
248*4882a593Smuzhiyun         title.addClass("default-orderby");
249*4882a593Smuzhiyun       }
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun      } else {
252*4882a593Smuzhiyun        /* Not orderable */
253*4882a593Smuzhiyun        header.css("font-weight", "normal");
254*4882a593Smuzhiyun        header.append('<span class="text-muted">' + col.title + '</span> ');
255*4882a593Smuzhiyun      }
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun      /* Setup the filter button */
258*4882a593Smuzhiyun      if (col.filter_name){
259*4882a593Smuzhiyun        var filterBtn = $('<a href="#" role="button" data-filter-on="' + col.filter_name + '" class="pull-right btn btn-link btn-xs" data-toggle="modal"><i class="glyphicon glyphicon-filter filtered"></i></a>');
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun        filterBtn.data('filter-name', col.filter_name);
262*4882a593Smuzhiyun        filterBtn.prop('id', col.filter_name);
263*4882a593Smuzhiyun        filterBtn.click(filterOpenClicked);
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun        /* If we're currently being filtered setup the visial indicator */
266*4882a593Smuzhiyun        if (tableParams.filter &&
267*4882a593Smuzhiyun            tableParams.filter.match('^'+col.filter_name)) {
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun            filterBtnActive(filterBtn, true);
270*4882a593Smuzhiyun        }
271*4882a593Smuzhiyun        header.append(filterBtn);
272*4882a593Smuzhiyun      }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun      /* Done making the header now add it */
275*4882a593Smuzhiyun      tableHeadRow.append(header);
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun      /* Now setup the checkbox state and click handler */
278*4882a593Smuzhiyun      var toggler = $('<li><div class="checkbox"><label><input type="checkbox" id="checkbox-'+ col.field_name +'" class="col-toggle" value="'+col.field_name+'" />'+col.title+'</label></div></li>');
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun      var togglerInput = toggler.find("input");
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun      togglerInput.attr("checked","checked");
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun      /* If we can hide the column enable the checkbox action */
285*4882a593Smuzhiyun      if (col.hideable){
286*4882a593Smuzhiyun        togglerInput.click(colToggleClicked);
287*4882a593Smuzhiyun      } else {
288*4882a593Smuzhiyun        toggler.find("label").addClass("text-muted");
289*4882a593Smuzhiyun        toggler.find("label").parent().addClass("disabled");
290*4882a593Smuzhiyun        togglerInput.attr("disabled", "disabled");
291*4882a593Smuzhiyun      }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun      if (col.hidden) {
294*4882a593Smuzhiyun        defaultHiddenCols.push(col.field_name);
295*4882a593Smuzhiyun      }
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun      /* Gather the Edit Column entries */
298*4882a593Smuzhiyun      column_edit_entries.push({'title':col.title,'html':toggler});
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun    } /* End for each column */
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun    /* Append the sorted Edit Column toggler entries */
303*4882a593Smuzhiyun    column_edit_entries.sort(function(a,b) {return (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0);} );
304*4882a593Smuzhiyun    for (var col in column_edit_entries){
305*4882a593Smuzhiyun      editColMenu.append(column_edit_entries[col].html);
306*4882a593Smuzhiyun    }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun    tableChromeDone = true;
309*4882a593Smuzhiyun  }
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun  /* Toggles the active state of the filter button */
312*4882a593Smuzhiyun  function filterBtnActive(filterBtn, active){
313*4882a593Smuzhiyun    if (active) {
314*4882a593Smuzhiyun      filterBtn.removeClass("btn-link");
315*4882a593Smuzhiyun      filterBtn.addClass("btn-primary");
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun      filterBtn.tooltip({
318*4882a593Smuzhiyun          html: true,
319*4882a593Smuzhiyun          title: '<button class="btn btn-sm btn-primary" onClick=\'$("#clear-filter-btn-'+ ctx.tableName +'").click();\'>Clear filter</button>',
320*4882a593Smuzhiyun          placement: 'bottom',
321*4882a593Smuzhiyun          delay: {
322*4882a593Smuzhiyun            hide: 1500,
323*4882a593Smuzhiyun            show: 400,
324*4882a593Smuzhiyun          },
325*4882a593Smuzhiyun      });
326*4882a593Smuzhiyun    } else {
327*4882a593Smuzhiyun      filterBtn.removeClass("btn-primary");
328*4882a593Smuzhiyun      filterBtn.addClass("btn-link");
329*4882a593Smuzhiyun      filterBtn.tooltip('destroy');
330*4882a593Smuzhiyun    }
331*4882a593Smuzhiyun  }
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun  /* Display or hide table columns based on the cookie preference or defaults */
334*4882a593Smuzhiyun  function loadColumnsPreference(){
335*4882a593Smuzhiyun    var cookie_data = $.cookie("cols");
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun    if (cookie_data) {
338*4882a593Smuzhiyun      var cols_hidden = JSON.parse($.cookie("cols"));
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun      /* For each of the columns check if we should hide them
341*4882a593Smuzhiyun       * also update the checked status in the Edit columns menu
342*4882a593Smuzhiyun       */
343*4882a593Smuzhiyun      $("#"+ctx.tableName+" th").each(function(){
344*4882a593Smuzhiyun        for (var i in cols_hidden){
345*4882a593Smuzhiyun          if ($(this).hasClass(cols_hidden[i])){
346*4882a593Smuzhiyun            table.find("."+cols_hidden[i]).hide();
347*4882a593Smuzhiyun            $("#checkbox-"+cols_hidden[i]).removeAttr("checked");
348*4882a593Smuzhiyun          }
349*4882a593Smuzhiyun        }
350*4882a593Smuzhiyun      });
351*4882a593Smuzhiyun      } else {
352*4882a593Smuzhiyun        /* Disable these columns by default when we have no columns
353*4882a593Smuzhiyun         * user setting.
354*4882a593Smuzhiyun         */
355*4882a593Smuzhiyun        for (var i in defaultHiddenCols) {
356*4882a593Smuzhiyun          table.find("."+defaultHiddenCols[i]).hide();
357*4882a593Smuzhiyun          $("#checkbox-"+defaultHiddenCols[i]).removeAttr("checked");
358*4882a593Smuzhiyun        }
359*4882a593Smuzhiyun    }
360*4882a593Smuzhiyun  }
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun  /* Apply an ordering to the current table.
363*4882a593Smuzhiyun   *
364*4882a593Smuzhiyun   * 1. Find the column heading matching the sortSpecifier
365*4882a593Smuzhiyun   * 2. Set its up/down arrow and add .sorted
366*4882a593Smuzhiyun   *
367*4882a593Smuzhiyun   * orderby: e.g. "-started_on", "completed_on"
368*4882a593Smuzhiyun   * colHeading: column heading element to activate (by showing the caret
369*4882a593Smuzhiyun   * up/down, depending on sort order); if not set, the correct column
370*4882a593Smuzhiyun   * heading is selected from the DOM using orderby as a key
371*4882a593Smuzhiyun   */
372*4882a593Smuzhiyun  function applyOrderby(orderby, colHeading) {
373*4882a593Smuzhiyun    if (!orderby) {
374*4882a593Smuzhiyun      return;
375*4882a593Smuzhiyun    }
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun    // We only have one sort at a time so remove existing sort indicators
378*4882a593Smuzhiyun    $("#" + ctx.tableName + " th .icon-caret-down").hide();
379*4882a593Smuzhiyun    $("#" + ctx.tableName + " th .icon-caret-up").hide();
380*4882a593Smuzhiyun    $("#" + ctx.tableName + " th a").removeClass("sorted");
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun    // normalise the orderby so we can use it to find the link we want
383*4882a593Smuzhiyun    // to style
384*4882a593Smuzhiyun    var fieldName = orderby;
385*4882a593Smuzhiyun    if (fieldName.indexOf('-') === 0) {
386*4882a593Smuzhiyun      fieldName = fieldName.slice(1);
387*4882a593Smuzhiyun    }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun    // find the table header element which corresponds to the sort field
390*4882a593Smuzhiyun    // (if we don't already have it)
391*4882a593Smuzhiyun    if (!colHeading) {
392*4882a593Smuzhiyun      colHeading = $('[data-sort-field="' + fieldName + '"]');
393*4882a593Smuzhiyun    }
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun    colHeading.addClass("sorted");
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun    var parent = colHeading.parent();
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun    if (orderby.indexOf('-') === 0) {
400*4882a593Smuzhiyun      parent.children('.icon-caret-up').show();
401*4882a593Smuzhiyun    }
402*4882a593Smuzhiyun    else {
403*4882a593Smuzhiyun      parent.children('.icon-caret-down').show();
404*4882a593Smuzhiyun    }
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun    tableParams.orderby = orderby;
407*4882a593Smuzhiyun    loadData(tableParams);
408*4882a593Smuzhiyun  }
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun  function sortColumnClicked(e){
411*4882a593Smuzhiyun    e.preventDefault();
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun    /* if we're already sorted sort the other way */
414*4882a593Smuzhiyun    var orderby = $(this).data('field-name');
415*4882a593Smuzhiyun    if (tableParams.orderby === orderby &&
416*4882a593Smuzhiyun        tableParams.orderby.indexOf('-') === -1) {
417*4882a593Smuzhiyun      orderby = '-' + orderby;
418*4882a593Smuzhiyun    }
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun    applyOrderby(orderby, $(this));
421*4882a593Smuzhiyun  }
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun  function pageButtonClicked(e) {
424*4882a593Smuzhiyun    tableParams.page = Number($(this).text());
425*4882a593Smuzhiyun    loadData(tableParams);
426*4882a593Smuzhiyun    /* Stop page jumps when clicking on # links */
427*4882a593Smuzhiyun    e.preventDefault();
428*4882a593Smuzhiyun  }
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun  /* Toggle a table column */
431*4882a593Smuzhiyun  function colToggleClicked (){
432*4882a593Smuzhiyun    var col = $(this).val();
433*4882a593Smuzhiyun    var disabled_cols = [];
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun    if ($(this).prop("checked")) {
436*4882a593Smuzhiyun      table.find("."+col).show();
437*4882a593Smuzhiyun    }  else {
438*4882a593Smuzhiyun      table.find("."+col).hide();
439*4882a593Smuzhiyun      // If we're ordered by the column we're hiding remove the order by
440*4882a593Smuzhiyun      // and apply the default one instead
441*4882a593Smuzhiyun      if (col === tableParams.orderby ||
442*4882a593Smuzhiyun          '-' + col === tableParams.orderby){
443*4882a593Smuzhiyun        tableParams.orderby = null;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun        applyOrderby(tableParams.default_orderby);
446*4882a593Smuzhiyun      }
447*4882a593Smuzhiyun    }
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun    /* Update the cookie with the unchecked columns */
450*4882a593Smuzhiyun    $(".col-toggle").not(":checked").map(function(){
451*4882a593Smuzhiyun      disabled_cols.push($(this).val());
452*4882a593Smuzhiyun    });
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun    $.cookie("cols", JSON.stringify(disabled_cols));
455*4882a593Smuzhiyun  }
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun  /**
458*4882a593Smuzhiyun   * Create the DOM/JS for the client side of a TableFilterActionToggle
459*4882a593Smuzhiyun   * or TableFilterActionDay
460*4882a593Smuzhiyun   *
461*4882a593Smuzhiyun   * filterName: (string) internal name for the filter action
462*4882a593Smuzhiyun   * filterActionData: (object)
463*4882a593Smuzhiyun   * filterActionData.count: (number) The number of items this filter will
464*4882a593Smuzhiyun   * show when selected
465*4882a593Smuzhiyun   *
466*4882a593Smuzhiyun   * NB this triggers a filtervalue event each time its radio button is checked
467*4882a593Smuzhiyun   */
468*4882a593Smuzhiyun  function createActionRadio(filterName, filterActionData) {
469*4882a593Smuzhiyun    var hasNoRecords = (Number(filterActionData.count) == 0);
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun    var actionStr = '<div class="radio">' +
472*4882a593Smuzhiyun      '<label class="filter-title' +
473*4882a593Smuzhiyun      (hasNoRecords ? ' text-muted' : '') + '"' +
474*4882a593Smuzhiyun      '       for="' + filterName + '">' +
475*4882a593Smuzhiyun      '<input type="radio" name="filter"' +
476*4882a593Smuzhiyun      '       value="' + filterName + '"';
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun    if (hasNoRecords) {
479*4882a593Smuzhiyun      actionStr += ' disabled="disabled"';
480*4882a593Smuzhiyun    }
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun    actionStr += ' id="' + filterName + '">' +
483*4882a593Smuzhiyun      '<input type="hidden" name="filter_value" value="on"' +
484*4882a593Smuzhiyun      '       data-value-for="' + filterName + '">' +
485*4882a593Smuzhiyun      filterActionData.title +
486*4882a593Smuzhiyun      ' (' + filterActionData.count + ')' +
487*4882a593Smuzhiyun      '</label>' +
488*4882a593Smuzhiyun      '</div>';
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun    var action = $(actionStr);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun    // fire the filtervalue event from this action when the radio button
493*4882a593Smuzhiyun    // is active so that the apply button can be enabled
494*4882a593Smuzhiyun    action.find('[type="radio"]').change(function () {
495*4882a593Smuzhiyun      if ($(this).is(':checked')) {
496*4882a593Smuzhiyun        action.trigger('filtervalue', 'on');
497*4882a593Smuzhiyun      }
498*4882a593Smuzhiyun    });
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun    return action;
501*4882a593Smuzhiyun  }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun  /**
504*4882a593Smuzhiyun   * Create the DOM/JS for the client side of a TableFilterActionDateRange
505*4882a593Smuzhiyun   *
506*4882a593Smuzhiyun   * filterName: (string) internal name for the filter action
507*4882a593Smuzhiyun   * filterValue: (string) from,to date range in format yyyy-mm-dd,yyyy-mm-dd;
508*4882a593Smuzhiyun   * used to select the current values for the from/to datepickers;
509*4882a593Smuzhiyun   * if this is partial (e.g. "yyyy-mm-dd,") only the applicable datepicker
510*4882a593Smuzhiyun   * will have a date pre-selected; if empty, neither will
511*4882a593Smuzhiyun   * filterActionData: (object) data for generating the action's HTML
512*4882a593Smuzhiyun   * filterActionData.title: label for the radio button
513*4882a593Smuzhiyun   * filterActionData.max: (string) maximum date for the pickers, in ISO 8601
514*4882a593Smuzhiyun   * datetime format
515*4882a593Smuzhiyun   * filterActionData.min: (string) minimum date for the pickers, ISO 8601
516*4882a593Smuzhiyun   * datetime
517*4882a593Smuzhiyun   *
518*4882a593Smuzhiyun   * NB this triggers a filtervalue event each time its radio button is checked
519*4882a593Smuzhiyun   */
520*4882a593Smuzhiyun  function createActionDateRange(filterName, filterValue, filterActionData) {
521*4882a593Smuzhiyun    var action = $('<div class="radio">' +
522*4882a593Smuzhiyun                   '<label class="filter-title"' +
523*4882a593Smuzhiyun                   '       for="' + filterName + '">' +
524*4882a593Smuzhiyun                   '<input type="radio" name="filter"' +
525*4882a593Smuzhiyun                   '       value="' + filterName + '" ' +
526*4882a593Smuzhiyun                   '       id="' + filterName + '">' +
527*4882a593Smuzhiyun                   '<input type="hidden" name="filter_value" value=""' +
528*4882a593Smuzhiyun                   '       data-value-for="' + filterName + '">' +
529*4882a593Smuzhiyun                   filterActionData.title +
530*4882a593Smuzhiyun                   '</label>' +
531*4882a593Smuzhiyun									 '<div class="form-inline form-group date-filter-controls">' +
532*4882a593Smuzhiyun                   '<input type="text" maxlength="10" class="form-control"' +
533*4882a593Smuzhiyun                   '       data-date-from-for="' + filterName + '">' +
534*4882a593Smuzhiyun                   '<span>to</span>' +
535*4882a593Smuzhiyun                   '<input type="text" maxlength="10" class="form-control"' +
536*4882a593Smuzhiyun                   '       data-date-to-for="' + filterName + '">' +
537*4882a593Smuzhiyun                   '<span class="help-inline get-help">(yyyy-mm-dd)</span>' +
538*4882a593Smuzhiyun                   '</div></div>');
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun    var radio = action.find('[type="radio"]');
541*4882a593Smuzhiyun    var value = action.find('[data-value-for]');
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun    // make the datepickers for the range
544*4882a593Smuzhiyun    var options = {
545*4882a593Smuzhiyun      dateFormat: 'yy-mm-dd',
546*4882a593Smuzhiyun      maxDate: new Date(filterActionData.max),
547*4882a593Smuzhiyun      minDate: new Date(filterActionData.min)
548*4882a593Smuzhiyun    };
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun    // create date pickers, setting currently-selected from and to dates
551*4882a593Smuzhiyun    var selectedFrom = null;
552*4882a593Smuzhiyun    var selectedTo = null;
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun    var selectedFromAndTo = [];
555*4882a593Smuzhiyun    if (filterValue) {
556*4882a593Smuzhiyun      selectedFromAndTo = filterValue.split(',');
557*4882a593Smuzhiyun    }
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun    if (selectedFromAndTo.length == 2) {
560*4882a593Smuzhiyun      selectedFrom = selectedFromAndTo[0];
561*4882a593Smuzhiyun      selectedTo = selectedFromAndTo[1];
562*4882a593Smuzhiyun    }
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun    options.defaultDate = selectedFrom;
565*4882a593Smuzhiyun    var inputFrom =
566*4882a593Smuzhiyun      action.find('[data-date-from-for]').datepicker(options);
567*4882a593Smuzhiyun    inputFrom.val(selectedFrom);
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun    options.defaultDate = selectedTo;
570*4882a593Smuzhiyun    var inputTo =
571*4882a593Smuzhiyun      action.find('[data-date-to-for]').datepicker(options);
572*4882a593Smuzhiyun    inputTo.val(selectedTo);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun    // set filter_value based on date pickers when
575*4882a593Smuzhiyun    // one of their values changes; if either from or to are unset,
576*4882a593Smuzhiyun    // the new value is null;
577*4882a593Smuzhiyun    // this triggers a 'filter_value-change' event on the action's element,
578*4882a593Smuzhiyun    // which is used to determine the disabled/enabled state of the "Apply"
579*4882a593Smuzhiyun    // button
580*4882a593Smuzhiyun    var changeHandler = function () {
581*4882a593Smuzhiyun      var fromValue = inputFrom.val();
582*4882a593Smuzhiyun      var toValue = inputTo.val();
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun      var newValue = undefined;
585*4882a593Smuzhiyun      if (fromValue !== '' && toValue !== '') {
586*4882a593Smuzhiyun        newValue = fromValue + ',' + toValue;
587*4882a593Smuzhiyun      }
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun      value.val(newValue);
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun      // if this action is selected, fire an event for the new range
592*4882a593Smuzhiyun      if (radio.is(':checked')) {
593*4882a593Smuzhiyun        action.trigger('filtervalue', newValue);
594*4882a593Smuzhiyun      }
595*4882a593Smuzhiyun    };
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun    inputFrom.change(changeHandler);
598*4882a593Smuzhiyun    inputTo.change(changeHandler);
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun    // check the associated radio button on clicking a date picker
601*4882a593Smuzhiyun    var checkRadio = function () {
602*4882a593Smuzhiyun      radio.prop('checked', 'checked');
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun      // checking the radio button this way doesn't cause the "change"
605*4882a593Smuzhiyun      // event to fire, so we manually call the changeHandler
606*4882a593Smuzhiyun      changeHandler();
607*4882a593Smuzhiyun    };
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun    inputFrom.focus(checkRadio);
610*4882a593Smuzhiyun    inputTo.focus(checkRadio);
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun    // selecting a date in a picker constrains the date you can
613*4882a593Smuzhiyun    // set in the other picker
614*4882a593Smuzhiyun    inputFrom.change(function () {
615*4882a593Smuzhiyun      inputTo.datepicker('option', 'minDate', inputFrom.val());
616*4882a593Smuzhiyun    });
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun    inputTo.change(function () {
619*4882a593Smuzhiyun      inputFrom.datepicker('option', 'maxDate', inputTo.val());
620*4882a593Smuzhiyun    });
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun    // checking the radio input causes the "Apply" button disabled state to
623*4882a593Smuzhiyun    // change, depending on which from/to dates are supplied
624*4882a593Smuzhiyun    radio.change(changeHandler);
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun    return action;
627*4882a593Smuzhiyun  }
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun  function filterOpenClicked(){
630*4882a593Smuzhiyun    var filterName = $(this).data('filter-name');
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun    /* We need to pass in the current search so that the filter counts take
633*4882a593Smuzhiyun     * into account the current search term
634*4882a593Smuzhiyun     */
635*4882a593Smuzhiyun    var params = {
636*4882a593Smuzhiyun      'name' : filterName,
637*4882a593Smuzhiyun      'search': tableParams.search,
638*4882a593Smuzhiyun      'cmd': 'filterinfo',
639*4882a593Smuzhiyun    };
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun    $.ajax({
642*4882a593Smuzhiyun        type: "GET",
643*4882a593Smuzhiyun        url: ctx.url,
644*4882a593Smuzhiyun        data: params,
645*4882a593Smuzhiyun        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
646*4882a593Smuzhiyun        success: function (filterData) {
647*4882a593Smuzhiyun          /*
648*4882a593Smuzhiyun            filterData structure:
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun            {
651*4882a593Smuzhiyun              title: '<title for the filter popup>',
652*4882a593Smuzhiyun              filter_actions: [
653*4882a593Smuzhiyun                {
654*4882a593Smuzhiyun                  title: '<label for radio button inside the popup>',
655*4882a593Smuzhiyun                  name: '<name of the filter action>',
656*4882a593Smuzhiyun                  count: <number of items this filter will show>,
657*4882a593Smuzhiyun                  ... additional data for the action ...
658*4882a593Smuzhiyun                }
659*4882a593Smuzhiyun              ]
660*4882a593Smuzhiyun            }
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun            each filter_action gets a radio button; the value of this is
663*4882a593Smuzhiyun            set to filterName + ':' + filter_action.name; e.g.
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun              in_current_project:in_project
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun            specifies the "in_project" action of the "in_current_project"
668*4882a593Smuzhiyun            filter
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun            the filterName is set on the column filter icon, and corresponds
671*4882a593Smuzhiyun            to a value in the table's filter map
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun            when the filter popup's "Apply" button is clicked, the
674*4882a593Smuzhiyun            value for the radio button which is checked is passed in the
675*4882a593Smuzhiyun            querystring, along with a filter_value, and applied to the
676*4882a593Smuzhiyun            queryset on the table
677*4882a593Smuzhiyun          */
678*4882a593Smuzhiyun          var filterActionRadios = $('#filter-actions-' + ctx.tableName);
679*4882a593Smuzhiyun          var filterApplyBtn = $('[data-cat="filter-apply"]');
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun          var setApplyButtonState = function (e, filterActionValue) {
682*4882a593Smuzhiyun            if (filterActionValue !== undefined) {
683*4882a593Smuzhiyun              filterApplyBtn.removeAttr('disabled');
684*4882a593Smuzhiyun            }
685*4882a593Smuzhiyun            else {
686*4882a593Smuzhiyun              filterApplyBtn.attr('disabled', 'disabled');
687*4882a593Smuzhiyun            }
688*4882a593Smuzhiyun          };
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun          $('#filter-modal-title-' + ctx.tableName).text(filterData.title);
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun          filterActionRadios.empty();
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun          // create a radio button + form elements for each action associated
695*4882a593Smuzhiyun          // with the filter on this column of the table
696*4882a593Smuzhiyun          for (var i in filterData.filter_actions) {
697*4882a593Smuzhiyun            var action = null;
698*4882a593Smuzhiyun            var filterActionData = filterData.filter_actions[i];
699*4882a593Smuzhiyun            var filterName = filterData.name + ':' +
700*4882a593Smuzhiyun                             filterActionData.action_name;
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun            if (filterActionData.type === 'toggle' ||
703*4882a593Smuzhiyun                filterActionData.type === 'day') {
704*4882a593Smuzhiyun              action = createActionRadio(filterName, filterActionData);
705*4882a593Smuzhiyun            }
706*4882a593Smuzhiyun            else if (filterActionData.type === 'daterange') {
707*4882a593Smuzhiyun              // current values for the from/to dates
708*4882a593Smuzhiyun              var filterValue = tableParams.filter_value;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun              action = createActionDateRange(
711*4882a593Smuzhiyun                filterName,
712*4882a593Smuzhiyun                filterValue,
713*4882a593Smuzhiyun                filterActionData
714*4882a593Smuzhiyun              );
715*4882a593Smuzhiyun            }
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun            if (action) {
718*4882a593Smuzhiyun              // Setup the current selected filter; default to 'all' if
719*4882a593Smuzhiyun              // no current filter selected
720*4882a593Smuzhiyun              var radioInput = action.find('input[name="filter"]');
721*4882a593Smuzhiyun              if ((tableParams.filter &&
722*4882a593Smuzhiyun                  tableParams.filter === radioInput.val()) ||
723*4882a593Smuzhiyun                  filterActionData.action_name == 'all') {
724*4882a593Smuzhiyun                  radioInput.prop("checked", "checked");
725*4882a593Smuzhiyun              }
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun              filterActionRadios.append(action);
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun              // if the action's filter_value changes but is falsy, disable
730*4882a593Smuzhiyun              // the "Apply" button
731*4882a593Smuzhiyun              action.on('filtervalue', setApplyButtonState);
732*4882a593Smuzhiyun            }
733*4882a593Smuzhiyun          }
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun          $('#filter-modal-'+ctx.tableName).modal('show');
736*4882a593Smuzhiyun        }
737*4882a593Smuzhiyun    });
738*4882a593Smuzhiyun  }
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun  /* Allow pages to trigger reload event */
741*4882a593Smuzhiyun  table.on('reload', function(e, newTableParams){
742*4882a593Smuzhiyun    if (newTableParams)
743*4882a593Smuzhiyun      loadData(newTableParams);
744*4882a593Smuzhiyun    else
745*4882a593Smuzhiyun      loadData(tableParams)
746*4882a593Smuzhiyun  });
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun  $(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun  /* Keep the Edit columns menu open after click by eating the event */
751*4882a593Smuzhiyun  $('.dropdown-menu').click(function(e) {
752*4882a593Smuzhiyun    e.stopPropagation();
753*4882a593Smuzhiyun  });
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun  $(".pagesize-"+ctx.tableName).val(tableParams.limit);
756*4882a593Smuzhiyun
757*4882a593Smuzhiyun  /* page size selector  */
758*4882a593Smuzhiyun  $(".pagesize-"+ctx.tableName).change(function(e){
759*4882a593Smuzhiyun    tableParams.limit = Number(this.value);
760*4882a593Smuzhiyun    if ((tableParams.page * tableParams.limit) > tableTotal)
761*4882a593Smuzhiyun      tableParams.page = 1;
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun    loadData(tableParams);
764*4882a593Smuzhiyun    /* sync the other selectors on the page */
765*4882a593Smuzhiyun    $(".pagesize-"+ctx.tableName).val(this.value);
766*4882a593Smuzhiyun    e.preventDefault();
767*4882a593Smuzhiyun  });
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun  $("#search-submit-"+ctx.tableName).click(function(e){
770*4882a593Smuzhiyun    e.preventDefault();
771*4882a593Smuzhiyun    var searchTerm = $("#search-input-"+ctx.tableName).val();
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun    tableParams.page = 1;
774*4882a593Smuzhiyun    tableParams.search = searchTerm;
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun    /* If a filter was active we remove it */
777*4882a593Smuzhiyun    if (tableParams.filter) {
778*4882a593Smuzhiyun      var filterBtn = $("#" + tableParams.filter.split(":")[0]);
779*4882a593Smuzhiyun      filterBtnActive(filterBtn, false);
780*4882a593Smuzhiyun      tableParams.filter = null;
781*4882a593Smuzhiyun    }
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun    loadData(tableParams);
784*4882a593Smuzhiyun  });
785*4882a593Smuzhiyun
786*4882a593Smuzhiyun  clearSearchElements.click(function(e){
787*4882a593Smuzhiyun    e.preventDefault();
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun    tableParams.page = 1;
790*4882a593Smuzhiyun    tableParams.search = null;
791*4882a593Smuzhiyun    loadData(tableParams);
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun    $("#search-input-"+ctx.tableName).val("");
794*4882a593Smuzhiyun    $(this).hide();
795*4882a593Smuzhiyun  });
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun  $("#search-input-"+ctx.tableName).keyup(function(e){
798*4882a593Smuzhiyun    if (e.which === 13)
799*4882a593Smuzhiyun      $('#search-submit-'+ctx.tableName).click();
800*4882a593Smuzhiyun  });
801*4882a593Smuzhiyun
802*4882a593Smuzhiyun  /* Stop page jumps when clicking on # links */
803*4882a593Smuzhiyun  $('a[href="#"]').click(function(e){
804*4882a593Smuzhiyun    e.preventDefault();
805*4882a593Smuzhiyun  });
806*4882a593Smuzhiyun
807*4882a593Smuzhiyun  $("#clear-filter-btn-"+ctx.tableName).click(function(e){
808*4882a593Smuzhiyun    e.preventDefault();
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun    var filterBtn = $("#" + tableParams.filter.split(":")[0]);
811*4882a593Smuzhiyun    filterBtnActive(filterBtn, false);
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun    tableParams.filter = null;
814*4882a593Smuzhiyun    loadData(tableParams);
815*4882a593Smuzhiyun  });
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun  $("#filter-modal-form-"+ctx.tableName).submit(function(e){
818*4882a593Smuzhiyun    e.preventDefault();
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun    /* remove active status from all filter buttons so that only one filter
821*4882a593Smuzhiyun       can be active at a time */
822*4882a593Smuzhiyun    $('[data-filter-on]').each(function (index, filterBtn) {
823*4882a593Smuzhiyun      filterBtnActive($(filterBtn), false);
824*4882a593Smuzhiyun    });
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun    // checked radio button
827*4882a593Smuzhiyun    var checkedFilter = $(this).find("input[name='filter']:checked");
828*4882a593Smuzhiyun    tableParams.filter = checkedFilter.val();
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun    // hidden field holding the value for the checked filter
831*4882a593Smuzhiyun    var checkedFilterValue = $(this).find("input[data-value-for='" +
832*4882a593Smuzhiyun                                          tableParams.filter + "']");
833*4882a593Smuzhiyun    tableParams.filter_value = checkedFilterValue.val();
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun    /* All === remove filter */
836*4882a593Smuzhiyun    if (tableParams.filter.match(":all$")) {
837*4882a593Smuzhiyun      tableParams.filter = null;
838*4882a593Smuzhiyun      tableParams.filter_value = null;
839*4882a593Smuzhiyun    } else {
840*4882a593Smuzhiyun      var filterBtn = $("#" + tableParams.filter.split(":")[0]);
841*4882a593Smuzhiyun      filterBtnActive(filterBtn, true);
842*4882a593Smuzhiyun    }
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun    loadData(tableParams);
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun    $('#filter-modal-'+ctx.tableName).modal('hide');
848*4882a593Smuzhiyun  });
849*4882a593Smuzhiyun
850*4882a593Smuzhiyun  table.on("table-loading", function(){
851*4882a593Smuzhiyun    table.css("opacity", 0.5);
852*4882a593Smuzhiyun  });
853*4882a593Smuzhiyun
854*4882a593Smuzhiyun  table.on("table-done", function(){
855*4882a593Smuzhiyun    table.css("opacity", 1);
856*4882a593Smuzhiyun  })
857*4882a593Smuzhiyun}
858