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